diff mbox

Reject boolean/enum types in last arg of __builtin_*_overflow_p

Message ID 20160610190835.GF7387@tucnak.redhat.com
State New
Headers show

Commit Message

Jakub Jelinek June 10, 2016, 7:08 p.m. UTC
Hi!

As mentioned in PR71479, for __builtin_*_overflow we right now require
the last argument to be pointer to INTEGER_TYPE, not INTEGRAL_TYPE_P,
but for __builtin_*_overflow_p we were using INTEGRAL_TYPE_P.

For _Bool/bool, I'd think we could make it well defined if we wanted
(check if the infinite precision result is 0 or 1), but it is hardly useful
and as can be seen, right now we don't implement that (for bool we actually
check if the result fits into unsigned 8 bit integer, plus _Complex bool
is kind of weird), but for enums I think the definition on what is an enum
overflow is quite fuzzy, would it for C++ e.g. work differently between
-fstrict-enums and -fno-strict-enums, would it test overflows only on the
underlying type, or exact precision of the enum, something else?

So, I think it is better to continue what we've been doing already for
__builtin_*_overflow before.  We likely need to adjust the documentation
and possibly the diagnostics.  Maybe have one wording for !INTEGRAL_TYPE_P
and another wording for the != INTEGER_TYPE case (where it would complain
that the type is {pointer to,} {enum,bool,_Bool}?  Any preferences?

The patch below just uses the preexisting wording (where integral stood
for integer types including bool/enums, and integer for non-bool/enum
integer types), I'd just like to see it in soon so that people don't start
to rely on something that doesn't really work and is hard to define.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2016-06-10  Jakub Jelinek  <jakub@redhat.com>

	* c-common.c (check_builtin_function_arguments): Require last
	argument of BUILT_IN_*_OVERFLOW_P to have INTEGER_TYPE type.

	* c-c++-common/builtin-arith-overflow-1.c (f3): Adjust expected
	diagnostics.
	* g++.dg/ext/builtin-arith-overflow-1.C: Pass 0 instead of C
	as last argument to __builtin_add_overflow_p.


	Jakub

Comments

Martin Sebor June 10, 2016, 7:28 p.m. UTC | #1
On 06/10/2016 01:08 PM, Jakub Jelinek wrote:
> Hi!
>
> As mentioned in PR71479, for __builtin_*_overflow we right now require
> the last argument to be pointer to INTEGER_TYPE, not INTEGRAL_TYPE_P,
> but for __builtin_*_overflow_p we were using INTEGRAL_TYPE_P.
>
> For _Bool/bool, I'd think we could make it well defined if we wanted
> (check if the infinite precision result is 0 or 1), but it is hardly useful
> and as can be seen, right now we don't implement that (for bool we actually
> check if the result fits into unsigned 8 bit integer

I agree that computing the bool result isn't terribly useful when
the type is known, but allowing it could be handy in generic code
(macros, C _Generic, and C++ templates).  Rejecting it, OTOH, means
that users will have to work a little harder to avoid an error in
generic contexts.  Unless there is some potential problem that I'm
not thinking of I would suggest to accept bool.

IMO, the right semantics for bool are to match what the standards
specify (i.e., no overflow, and the result is a logical OR of the
operands).

Continuing to reject enumerated and complex integer types makes
sense to me.

Martin
Jakub Jelinek June 14, 2016, 8:33 a.m. UTC | #2
On Fri, Jun 10, 2016 at 01:28:55PM -0600, Martin Sebor wrote:
> IMO, the right semantics for bool are to match what the standards
> specify (i.e., no overflow, and the result is a logical OR of the
> operands).

???  It is far from that.
The documentation says that say for __builtin_add_overflow (x, y, &z), you compute
infinite precision signed result of x + y, cast the result to __typeof (z),
extend again to infinite precision signed result and compare to the value
before casting.  So, if z is bool/_Bool, then this means
signed_intNNN_t t = (signed_intNNN_t) x + (signed_intNNN_t) y;
z = (__typeof (z)) t;
ovf = (signed_intNNN_t) z != t;

But, unlike normal signed or unsigned integral types or char, cast to
bool/_Bool is special, it is actually a comparison != 0.
So, I'd say if we want to support arith overflow to bool/_Bool, it should be
above with:
signed_intNNN_t t = (signed_intNNN_t) x + (signed_intNNN_t) y;
z = t != 0;
ovf = (signed_intNNN_t) z != t;
so in the end, if we'd use
movb x, r
addb y, r
seto ovf
to compute whether the result overflowed 8-bits, we'd need to
or in (r & 254) != 0 into the ovf and then or in ovf into r.
That means changes everywhere where we handle the overflow builtins. :(

	Jakub
Joseph Myers June 14, 2016, 2:05 p.m. UTC | #3
On Tue, 14 Jun 2016, Jakub Jelinek wrote:

> But, unlike normal signed or unsigned integral types or char, cast to
> bool/_Bool is special, it is actually a comparison != 0.
> So, I'd say if we want to support arith overflow to bool/_Bool, it should be
> above with:
> signed_intNNN_t t = (signed_intNNN_t) x + (signed_intNNN_t) y;
> z = t != 0;
> ovf = (signed_intNNN_t) z != t;

And, I really don't see this as useful.  Rejecting boolean types there 
makes more sense to me.
Marek Polacek June 14, 2016, 2:10 p.m. UTC | #4
On Tue, Jun 14, 2016 at 02:05:12PM +0000, Joseph Myers wrote:
> On Tue, 14 Jun 2016, Jakub Jelinek wrote:
> 
> > But, unlike normal signed or unsigned integral types or char, cast to
> > bool/_Bool is special, it is actually a comparison != 0.
> > So, I'd say if we want to support arith overflow to bool/_Bool, it should be
> > above with:
> > signed_intNNN_t t = (signed_intNNN_t) x + (signed_intNNN_t) y;
> > z = t != 0;
> > ovf = (signed_intNNN_t) z != t;
> 
> And, I really don't see this as useful.  Rejecting boolean types there 
> makes more sense to me.

I also don't think supporting booleans is worth all the hassle.

	Marek
diff mbox

Patch

--- gcc/c-family/c-common.c.jj	2016-06-10 20:23:57.433135328 +0200
+++ gcc/c-family/c-common.c	2016-06-10 20:44:51.104848132 +0200
@@ -9999,13 +9999,19 @@  check_builtin_function_arguments (locati
       if (builtin_function_validate_nargs (loc, fndecl, nargs, 3))
 	{
 	  unsigned i;
-	  for (i = 0; i < 3; i++)
+	  for (i = 0; i < 2; i++)
 	    if (!INTEGRAL_TYPE_P (TREE_TYPE (args[i])))
 	      {
 		error_at (ARG_LOCATION (i), "argument %u in call to function "
 			  "%qE does not have integral type", i + 1, fndecl);
 		return false;
 	      }
+	  if (TREE_CODE (TREE_TYPE (args[i])) != INTEGER_TYPE)
+	    {
+	      error_at (ARG_LOCATION (i), "argument %u in call to function "
+			"%qE does not have integer type", i + 1, fndecl);
+	      return false;
+	    }
 	  return true;
 	}
       return false;
--- gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c.jj	2016-06-10 20:24:01.000000000 +0200
+++ gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c	2016-06-10 20:56:04.461095680 +0200
@@ -236,11 +236,11 @@  f3 (float fa, int a, _Complex long int c
   x += __builtin_sub_overflow_p (ca, b, eb);	/* { dg-error "argument 1 in call to function\[^\n\r]*does not have integral type" } */
   x += __builtin_mul_overflow_p (a, fb, bb);	/* { dg-error "argument 2 in call to function\[^\n\r]*does not have integral type" } */
   x += __builtin_add_overflow_p (a, pb, a);	/* { dg-error "argument 2 in call to function\[^\n\r]*does not have integral type" } */
-  x += __builtin_sub_overflow_p (a, eb, eb);
-  x += __builtin_mul_overflow_p (a, bb, bb);
-  x += __builtin_add_overflow_p (a, b, fa);	/* { dg-error "argument 3 in call to function\[^\n\r]*does not have integral type" } */
-  x += __builtin_sub_overflow_p (a, b, ca);	/* { dg-error "argument 3 in call to function\[^\n\r]*does not have integral type" } */
-  x += __builtin_mul_overflow_p (a, b, c);	/* { dg-error "argument 3 in call to function\[^\n\r]*does not have integral type" } */
+  x += __builtin_sub_overflow_p (a, eb, eb);	/* { dg-error "argument 3 in call to function\[^\n\r]*does not have integer type" } */
+  x += __builtin_mul_overflow_p (a, bb, bb);	/* { dg-error "argument 3 in call to function\[^\n\r]*does not have integer type" } */
+  x += __builtin_add_overflow_p (a, b, fa);	/* { dg-error "argument 3 in call to function\[^\n\r]*does not have integer type" } */
+  x += __builtin_sub_overflow_p (a, b, ca);	/* { dg-error "argument 3 in call to function\[^\n\r]*does not have integer type" } */
+  x += __builtin_mul_overflow_p (a, b, c);	/* { dg-error "argument 3 in call to function\[^\n\r]*does not have integer type" } */
   return x;
 }
 
--- gcc/testsuite/g++.dg/ext/builtin-arith-overflow-1.C.jj	2016-06-08 21:01:25.000000000 +0200
+++ gcc/testsuite/g++.dg/ext/builtin-arith-overflow-1.C	2016-06-10 20:55:04.836870794 +0200
@@ -1,11 +1,11 @@ 
 // { dg-do compile }
 
-enum A { B = 1, C = 2, D = __builtin_add_overflow_p (B, C, C) };
-int e[__builtin_add_overflow_p (B, C, C) + 1];
+enum A { B = 1, C = 2, D = __builtin_add_overflow_p (B, C, 0) };
+int e[__builtin_add_overflow_p (B, C, 0) + 1];
 template <int N> int foo (int);
 
 void
 bar ()
 {
-  foo <__builtin_add_overflow_p (B, C, C) + 1> (0);
+  foo <__builtin_add_overflow_p (B, C, 0) + 1> (0);
 }