re PR c++/70507 (integer overflow builtins not constant expressions)
authorMartin Sebor <msebor@redhat.com>
Wed, 8 Jun 2016 19:03:17 +0000 (19:03 +0000)
committerJakub Jelinek <jakub@gcc.gnu.org>
Wed, 8 Jun 2016 19:03:17 +0000 (21:03 +0200)
PR c++/70507
PR c/68120
* builtins.def (BUILT_IN_ADD_OVERFLOW_P, BUILT_IN_SUB_OVERFLOW_P,
BUILT_IN_MUL_OVERFLOW_P): New builtins.
* builtins.c: Include gimple-fold.h.
(fold_builtin_arith_overflow): Handle
BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
(fold_builtin_3): Likewise.
* doc/extend.texi (Integer Overflow Builtins): Document
__builtin_{add,sub,mul}_overflow_p.
gcc/c/
* c-typeck.c (convert_arguments): Don't promote last argument
of BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
gcc/cp/
* constexpr.c: Include gimple-fold.h.
(cxx_eval_internal_function): New function.
(cxx_eval_call_expression): Call it.
(potential_constant_expression_1): Handle integer arithmetic
overflow built-ins.
* tree.c (builtin_valid_in_constant_expr_p): Handle
BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
gcc/c-family/
* c-common.c (check_builtin_function_arguments): Handle
BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
gcc/testsuite/
* c-c++-common/builtin-arith-overflow-1.c: Add test cases.
* c-c++-common/builtin-arith-overflow-2.c: New test.
* g++.dg/ext/builtin-arith-overflow-1.C: New test.
* g++.dg/cpp0x/constexpr-arith-overflow.C: New test.
* g++.dg/cpp1y/constexpr-arith-overflow.C: New test.

Co-Authored-By: Jakub Jelinek <jakub@redhat.com>
From-SVN: r237238

17 files changed:
gcc/ChangeLog
gcc/builtins.c
gcc/builtins.def
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c/ChangeLog
gcc/c/c-typeck.c
gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/cp/tree.c
gcc/doc/extend.texi
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/builtin-arith-overflow-1.c
gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/builtin-arith-overflow-1.C [new file with mode: 0644]

index d3d5200d45cffa894b253658c605d97a898f515c..89c9f766cce988e287df45b6525d539144b35b2c 100644 (file)
@@ -1,3 +1,17 @@
+2016-06-08  Martin Sebor  <msebor@redhat.com>
+           Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/70507
+       PR c/68120
+       * builtins.def (BUILT_IN_ADD_OVERFLOW_P, BUILT_IN_SUB_OVERFLOW_P,
+       BUILT_IN_MUL_OVERFLOW_P): New builtins.
+       * builtins.c: Include gimple-fold.h.
+       (fold_builtin_arith_overflow): Handle
+       BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
+       (fold_builtin_3): Likewise.
+       * doc/extend.texi (Integer Overflow Builtins): Document
+       __builtin_{add,sub,mul}_overflow_p.
+
 2016-06-08  Jose E. Marchesi  <jose.marchesi@oracle.com>
 
        * config/sparc/driver-sparc.c (cpu_names): Fix the entry for the
index d51917616807003eb4363ca4b2807a002202c1f7..5d234a5c8278f587b132d31ba558567d6a83f905 100644 (file)
@@ -64,6 +64,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl-chkp.h"
 #include "internal-fn.h"
 #include "case-cfn-macros.h"
+#include "gimple-fold.h"
 
 
 struct target_builtins default_target_builtins;
@@ -7943,18 +7944,28 @@ fold_builtin_unordered_cmp (location_t loc, tree fndecl, tree arg0, tree arg1,
 /* Fold __builtin_{,s,u}{add,sub,mul}{,l,ll}_overflow, either into normal
    arithmetics if it can never overflow, or into internal functions that
    return both result of arithmetics and overflowed boolean flag in
-   a complex integer result, or some other check for overflow.  */
+   a complex integer result, or some other check for overflow.
+   Similarly fold __builtin_{add,sub,mul}_overflow_p to just the overflow
+   checking part of that.  */
 
 static tree
 fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
                             tree arg0, tree arg1, tree arg2)
 {
   enum internal_fn ifn = IFN_LAST;
-  tree type = TREE_TYPE (TREE_TYPE (arg2));
-  tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2);
+  /* The code of the expression corresponding to the type-generic
+     built-in, or ERROR_MARK for the type-specific ones.  */
+  enum tree_code opcode = ERROR_MARK;
+  bool ovf_only = false;
+
   switch (fcode)
     {
+    case BUILT_IN_ADD_OVERFLOW_P:
+      ovf_only = true;
+      /* FALLTHRU */
     case BUILT_IN_ADD_OVERFLOW:
+      opcode = PLUS_EXPR;
+      /* FALLTHRU */
     case BUILT_IN_SADD_OVERFLOW:
     case BUILT_IN_SADDL_OVERFLOW:
     case BUILT_IN_SADDLL_OVERFLOW:
@@ -7963,7 +7974,12 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
     case BUILT_IN_UADDLL_OVERFLOW:
       ifn = IFN_ADD_OVERFLOW;
       break;
+    case BUILT_IN_SUB_OVERFLOW_P:
+      ovf_only = true;
+      /* FALLTHRU */
     case BUILT_IN_SUB_OVERFLOW:
+      opcode = MINUS_EXPR;
+      /* FALLTHRU */
     case BUILT_IN_SSUB_OVERFLOW:
     case BUILT_IN_SSUBL_OVERFLOW:
     case BUILT_IN_SSUBLL_OVERFLOW:
@@ -7972,7 +7988,12 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
     case BUILT_IN_USUBLL_OVERFLOW:
       ifn = IFN_SUB_OVERFLOW;
       break;
+    case BUILT_IN_MUL_OVERFLOW_P:
+      ovf_only = true;
+      /* FALLTHRU */
     case BUILT_IN_MUL_OVERFLOW:
+      opcode = MULT_EXPR;
+      /* FALLTHRU */
     case BUILT_IN_SMUL_OVERFLOW:
     case BUILT_IN_SMULL_OVERFLOW:
     case BUILT_IN_SMULLL_OVERFLOW:
@@ -7984,6 +8005,25 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
     default:
       gcc_unreachable ();
     }
+
+  /* For the "generic" overloads, the first two arguments can have different
+     types and the last argument determines the target type to use to check
+     for overflow.  The arguments of the other overloads all have the same
+     type.  */
+  tree type = ovf_only ? TREE_TYPE (arg2) : TREE_TYPE (TREE_TYPE (arg2));
+
+  /* For the __builtin_{add,sub,mul}_overflow_p builtins, when the first two
+     arguments are constant, attempt to fold the built-in call into a constant
+     expression indicating whether or not it detected an overflow.  */
+  if (ovf_only
+      && TREE_CODE (arg0) == INTEGER_CST
+      && TREE_CODE (arg1) == INTEGER_CST)
+    /* Perform the computation in the target type and check for overflow.  */
+    return omit_one_operand_loc (loc, boolean_type_node,
+                                arith_overflowed_p (opcode, type, arg0, arg1)
+                                ? boolean_true_node : boolean_false_node,
+                                arg2);
+
   tree ctype = build_complex_type (type);
   tree call = build_call_expr_internal_loc (loc, ifn, ctype,
                                            2, arg0, arg1);
@@ -7991,6 +8031,11 @@ fold_builtin_arith_overflow (location_t loc, enum built_in_function fcode,
   tree intres = build1_loc (loc, REALPART_EXPR, type, tgt);
   tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
   ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
+
+  if (ovf_only)
+    return omit_one_operand_loc (loc, boolean_type_node, ovfres, arg2);
+
+  tree mem_arg2 = build_fold_indirect_ref_loc (loc, arg2);
   tree store
     = fold_build2_loc (loc, MODIFY_EXPR, void_type_node, mem_arg2, intres);
   return build2_loc (loc, COMPOUND_EXPR, boolean_type_node, store, ovfres);
@@ -8340,6 +8385,9 @@ fold_builtin_3 (location_t loc, tree fndecl,
     case BUILT_IN_ADD_OVERFLOW:
     case BUILT_IN_SUB_OVERFLOW:
     case BUILT_IN_MUL_OVERFLOW:
+    case BUILT_IN_ADD_OVERFLOW_P:
+    case BUILT_IN_SUB_OVERFLOW_P:
+    case BUILT_IN_MUL_OVERFLOW_P:
     case BUILT_IN_SADD_OVERFLOW:
     case BUILT_IN_SADDL_OVERFLOW:
     case BUILT_IN_SADDLL_OVERFLOW:
index 527503800ffa82f0b94f881eca089b028db5b83f..ca9bafcbf2fecf0345851e40f763d3720a1a0c11 100644 (file)
@@ -710,6 +710,9 @@ DEF_C94_BUILTIN        (BUILT_IN_TOWUPPER, "towupper", BT_FN_WINT_WINT, ATTR_PUR
 DEF_GCC_BUILTIN        (BUILT_IN_ADD_OVERFLOW, "add_overflow", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
 DEF_GCC_BUILTIN        (BUILT_IN_SUB_OVERFLOW, "sub_overflow", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
 DEF_GCC_BUILTIN        (BUILT_IN_MUL_OVERFLOW, "mul_overflow", BT_FN_BOOL_VAR, ATTR_NOTHROW_TYPEGENERIC_LEAF)
+DEF_GCC_BUILTIN        (BUILT_IN_ADD_OVERFLOW_P, "add_overflow_p", BT_FN_BOOL_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
+DEF_GCC_BUILTIN        (BUILT_IN_SUB_OVERFLOW_P, "sub_overflow_p", BT_FN_BOOL_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
+DEF_GCC_BUILTIN        (BUILT_IN_MUL_OVERFLOW_P, "mul_overflow_p", BT_FN_BOOL_VAR, ATTR_CONST_NOTHROW_TYPEGENERIC_LEAF)
 /* Clang compatibility.  */
 DEF_GCC_BUILTIN        (BUILT_IN_SADD_OVERFLOW, "sadd_overflow", BT_FN_BOOL_INT_INT_INTPTR, ATTR_NOTHROW_LEAF_LIST)
 DEF_GCC_BUILTIN        (BUILT_IN_SADDL_OVERFLOW, "saddl_overflow", BT_FN_BOOL_LONG_LONG_LONGPTR, ATTR_NOTHROW_LEAF_LIST)
index 1cd8aa7ad32c2b713e2e9bd14bf14a95d900c997..5ebeaf90ff5d0b9d47f1367455ca965a99535d63 100644 (file)
@@ -1,3 +1,11 @@
+2016-06-08  Martin Sebor  <msebor@redhat.com>
+           Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/70507
+       PR c/68120
+       * c-common.c (check_builtin_function_arguments): Handle
+       BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
+
 2016-06-08  Richard Biener  <rguenther@suse.de>
 
        * c-common.c (parse_optimize_options): Improve diagnostic messages.
index 4e75e51c0134fd5565d87d79ed2b7ce5cea301a1..d008b040d0d071cd7b2c0a4645f355ec69feb3b3 100644 (file)
@@ -9989,6 +9989,23 @@ check_builtin_function_arguments (location_t loc, vec<location_t> arg_loc,
        }
       return false;
 
+    case BUILT_IN_ADD_OVERFLOW_P:
+    case BUILT_IN_SUB_OVERFLOW_P:
+    case BUILT_IN_MUL_OVERFLOW_P:
+      if (builtin_function_validate_nargs (loc, fndecl, nargs, 3))
+       {
+         unsigned i;
+         for (i = 0; i < 3; 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;
+             }
+         return true;
+       }
+      return false;
+
     default:
       return true;
     }
index 9f0b91afb6af5864cd0f9722100856da1df8e247..4d302453ee474fab106f78db9df53467d4158de8 100644 (file)
@@ -1,3 +1,11 @@
+2016-06-08  Martin Sebor  <msebor@redhat.com>
+           Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/70507
+       PR c/68120
+       * c-typeck.c (convert_arguments): Don't promote last argument
+       of BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
+
 2016-06-08  Marek Polacek  <polacek@redhat.com>
 
        PR c/71418
index cd8e9e57b3a06d5e75ec2b42698369302bba8d57..a681d7696d0d670bb6a658f09431ecb0f27fd863 100644 (file)
@@ -3186,6 +3186,7 @@ convert_arguments (location_t loc, vec<location_t> arg_loc, tree typelist,
   const bool type_generic = fundecl
     && lookup_attribute ("type generic", TYPE_ATTRIBUTES (TREE_TYPE (fundecl)));
   bool type_generic_remove_excess_precision = false;
+  bool type_generic_overflow_p = false;
   tree selector;
 
   /* Change pointer to function to the function itself for
@@ -3215,8 +3216,15 @@ convert_arguments (location_t loc, vec<location_t> arg_loc, tree typelist,
          type_generic_remove_excess_precision = true;
          break;
 
+       case BUILT_IN_ADD_OVERFLOW_P:
+       case BUILT_IN_SUB_OVERFLOW_P:
+       case BUILT_IN_MUL_OVERFLOW_P:
+         /* The last argument of these type-generic builtins
+            should not be promoted.  */
+         type_generic_overflow_p = true;
+         break;
+
        default:
-         type_generic_remove_excess_precision = false;
          break;
        }
     }
@@ -3466,9 +3474,12 @@ convert_arguments (location_t loc, vec<location_t> arg_loc, tree typelist,
              parmval = convert (double_type_node, val);
            }
        }
-      else if (excess_precision && !type_generic)
+      else if ((excess_precision && !type_generic)
+              || (type_generic_overflow_p && parmnum == 2))
        /* A "double" argument with excess precision being passed
-          without a prototype or in variable arguments.  */
+          without a prototype or in variable arguments.
+          The last argument of __builtin_*_overflow_p should not be
+          promoted.  */
        parmval = convert (valtype, val);
       else if ((invalid_func_diag =
                targetm.calls.invalid_arg_for_unprototyped_fn (typelist, fundecl, val)))
index 55b473e0dcc02de2d5eceef677e3d5e6d76e7f5d..4162e20ded7565b70575de474ee067aa0fe45921 100644 (file)
@@ -1,3 +1,16 @@
+2016-06-08  Martin Sebor  <msebor@redhat.com>
+           Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/70507
+       PR c/68120
+       * constexpr.c: Include gimple-fold.h.
+       (cxx_eval_internal_function): New function.
+       (cxx_eval_call_expression): Call it.
+       (potential_constant_expression_1): Handle integer arithmetic
+       overflow built-ins.
+       * tree.c (builtin_valid_in_constant_expr_p): Handle
+       BUILT_IN_{ADD,SUB,MUL}_OVERFLOW_P.
+
 2016-06-08  Paolo Carlini  <paolo.carlini@oracle.com>
 
        * pt.c (tsubst, case TYPENAME_TYPE): Don't delay checking the
index 482f8afaeb6d07a967434c964c9bbc178a4dd44b..ba40435ef67d4ae130a83b5d0c5b809723027d9b 100644 (file)
@@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "tree-inline.h"
 #include "ubsan.h"
+#include "gimple-fold.h"
 
 static bool verify_constant (tree, bool, bool *, bool *);
 #define VERIFY_CONSTANT(X)                                             \
@@ -1255,6 +1256,69 @@ cx_error_context (void)
   return r;
 }
 
+/* Evaluate a call T to a GCC internal function when possible and return
+   the evaluated result or, under the control of CTX, give an error, set
+   NON_CONSTANT_P, and return the unevaluated call T otherwise.  */
+
+static tree
+cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
+                           bool lval,
+                           bool *non_constant_p, bool *overflow_p)
+{
+  enum tree_code opcode = ERROR_MARK;
+
+  switch (CALL_EXPR_IFN (t))
+    {
+    case IFN_UBSAN_NULL:
+    case IFN_UBSAN_BOUNDS:
+    case IFN_UBSAN_VPTR:
+      return void_node;
+
+    case IFN_ADD_OVERFLOW:
+      opcode = PLUS_EXPR;
+      break;
+    case IFN_SUB_OVERFLOW:
+      opcode = MINUS_EXPR;
+      break;
+    case IFN_MUL_OVERFLOW:
+      opcode = MULT_EXPR;
+      break;
+
+    default:
+      if (!ctx->quiet)
+       error_at (EXPR_LOC_OR_LOC (t, input_location),
+                 "call to internal function %qE", t);
+      *non_constant_p = true;
+      return t;
+    }
+
+  /* Evaluate constant arguments using OPCODE and return a complex
+     number containing the result and the overflow bit.  */
+  tree arg0 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), lval,
+                                           non_constant_p, overflow_p);
+  tree arg1 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 1), lval,
+                                           non_constant_p, overflow_p);
+
+  if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
+    {
+      location_t loc = EXPR_LOC_OR_LOC (t, input_location);
+      tree type = TREE_TYPE (TREE_TYPE (t));
+      tree result = fold_binary_loc (loc, opcode, type,
+                                    fold_convert_loc (loc, type, arg0),
+                                    fold_convert_loc (loc, type, arg1));
+      tree ovf
+       = build_int_cst (type, arith_overflowed_p (opcode, type, arg0, arg1));
+      /* Reset TREE_OVERFLOW to avoid warnings for the overflow.  */
+      if (TREE_OVERFLOW (result))
+       TREE_OVERFLOW (result) = 0;
+
+      return build_complex (TREE_TYPE (t), result, ovf);
+    }
+
+  *non_constant_p = true;
+  return t;
+}
+
 /* Subroutine of cxx_eval_constant_expression.
    Evaluate the call expression tree T in the context of OLD_CALL expression
    evaluation.  */
@@ -1270,18 +1334,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
   bool depth_ok;
 
   if (fun == NULL_TREE)
-    switch (CALL_EXPR_IFN (t))
-      {
-      case IFN_UBSAN_NULL:
-      case IFN_UBSAN_BOUNDS:
-      case IFN_UBSAN_VPTR:
-       return void_node;
-      default:
-       if (!ctx->quiet)
-         error_at (loc, "call to internal function");
-       *non_constant_p = true;
-       return t;
-      }
+    return cxx_eval_internal_function (ctx, t, lval,
+                                      non_constant_p, overflow_p);
 
   if (TREE_CODE (fun) != FUNCTION_DECL)
     {
@@ -4588,6 +4642,10 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
 
        if (fun == NULL_TREE)
          {
+           /* Reset to allow the function to continue past the end
+              of the block below.  Otherwise return early.  */
+           bool bail = true;
+
            if (TREE_CODE (t) == CALL_EXPR
                && CALL_EXPR_FN (t) == NULL_TREE)
              switch (CALL_EXPR_IFN (t))
@@ -4598,16 +4656,27 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
                case IFN_UBSAN_BOUNDS:
                case IFN_UBSAN_VPTR:
                  return true;
+
+               case IFN_ADD_OVERFLOW:
+               case IFN_SUB_OVERFLOW:
+               case IFN_MUL_OVERFLOW:
+                 bail = false;
+
                default:
                  break;
                }
-           /* fold_call_expr can't do anything with IFN calls.  */
-           if (flags & tf_error)
-             error_at (EXPR_LOC_OR_LOC (t, input_location),
-                       "call to internal function");
-           return false;
+
+           if (bail)
+             {
+               /* fold_call_expr can't do anything with IFN calls.  */
+               if (flags & tf_error)
+                 error_at (EXPR_LOC_OR_LOC (t, input_location),
+                           "call to internal function %qE", t);
+               return false;
+             }
          }
-       if (is_overloaded_fn (fun))
+
+       if (fun && is_overloaded_fn (fun))
          {
            if (TREE_CODE (fun) == FUNCTION_DECL)
              {
@@ -4652,7 +4721,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
              i = num_artificial_parms_for (fun);
            fun = DECL_ORIGIN (fun);
          }
-       else
+       else if (fun)
           {
            if (RECUR (fun, rval))
              /* Might end up being a constant function pointer.  */;
index 04702ee1c006cae956f8183ffcb87182258d28b6..abda6e4f7294a488af588e8501d4d16f693bf0f9 100644 (file)
@@ -352,6 +352,12 @@ builtin_valid_in_constant_expr_p (const_tree decl)
     case BUILT_IN_FUNCTION:
     case BUILT_IN_LINE:
 
+      /* The following built-ins are valid in constant expressions
+        when their arguments are.  */
+    case BUILT_IN_ADD_OVERFLOW_P:
+    case BUILT_IN_SUB_OVERFLOW_P:
+    case BUILT_IN_MUL_OVERFLOW_P:
+
       /* These have constant results even if their operands are
         non-constant.  */
     case BUILT_IN_CONSTANT_P:
index 7208f33e76c9b508d7fa658dc31b12c57e4fcea6..7da516d7827161aebc1b73d0f97e62d47e5b210e 100644 (file)
@@ -9869,6 +9869,47 @@ functions above, except they perform multiplication, instead of addition.
 
 @end deftypefn
 
+The following built-in functions allow checking if simple arithmetic operation
+would overflow.
+
+@deftypefn {Built-in Function} bool __builtin_add_overflow_p (@var{type1} a, @var{type2} b, @var{type3} c)
+@deftypefnx {Built-in Function} bool __builtin_sub_overflow_p (@var{type1} a, @var{type2} b, @var{type3} c)
+@deftypefnx {Built-in Function} bool __builtin_mul_overflow_p (@var{type1} a, @var{type2} b, @var{type3} c)
+
+These built-in functions are similar to @code{__builtin_add_overflow},
+@code{__builtin_sub_overflow}, or @code{__builtin_mul_overflow}, except that
+they don't store the result of the arithmetic operation anywhere and the
+last argument is not a pointer, but some integral expression.
+
+The built-in functions promote the first two operands into infinite precision signed type
+and perform addition on those promoted operands. The result is then
+cast to the type of the third argument.  If the cast result is equal to the infinite
+precision result, the built-in functions return false, otherwise they return true.
+The value of the third argument is ignored, just the side-effects in the third argument
+are evaluated, and no integral argument promotions are performed on the last argument.
+
+For example, the following macro can be used to portably check, at
+compile-time, whether or not adding two constant integers will overflow,
+and perform the addition only when it is known to be safe and not to trigger
+a @option{-Woverflow} warning.
+
+@smallexample
+#define INT_ADD_OVERFLOW_P(a, b) \
+   __builtin_add_overflow_p (a, b, (__typeof__ ((a) + (b))) 0)
+
+enum @{
+    A = INT_MAX, B = 3,
+    C = INT_ADD_OVERFLOW_P (A, B) ? 0 : A + B,
+    D = __builtin_add_overflow_p (1, SCHAR_MAX, (signed char) 0)
+@};
+@end smallexample
+
+The compiler will attempt to use hardware instructions to implement
+these built-in functions where possible, like conditional jump on overflow
+after addition, conditional jump on carry etc.
+@end deftypefn
+
 @node x86 specific memory model extensions for transactional memory
 @section x86-Specific Memory Model Extensions for Transactional Memory
 
index 2f3e4e16766f3c56d6efd10b0060f2c64b3c8a3b..cb704589debb9b01a63d3eeb669f6a6dfc291912 100644 (file)
@@ -1,3 +1,14 @@
+2016-06-08  Martin Sebor  <msebor@redhat.com>
+           Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/70507
+       PR c/68120
+       * c-c++-common/builtin-arith-overflow-1.c: Add test cases.
+       * c-c++-common/builtin-arith-overflow-2.c: New test.
+       * g++.dg/ext/builtin-arith-overflow-1.C: New test.
+       * g++.dg/cpp0x/constexpr-arith-overflow.C: New test.
+       * g++.dg/cpp1y/constexpr-arith-overflow.C: New test.
+
 2016-06-08  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/71442
index 69b508386d5de3d6866c7489cd049232b4be9936..0d97f6a9fad4a5619ce22fd8c8f4d38539cfc5b1 100644 (file)
@@ -6,6 +6,9 @@ f1 (void)
   int x = __builtin_add_overflow ();   /* { dg-error "not enough arguments to function" } */
   x += __builtin_sub_overflow ();      /* { dg-error "not enough arguments to function" } */
   x += __builtin_mul_overflow ();      /* { dg-error "not enough arguments to function" } */
+  x += __builtin_add_overflow_p ();    /* { dg-error "not enough arguments to function" } */
+  x += __builtin_sub_overflow_p ();    /* { dg-error "not enough arguments to function" } */
+  x += __builtin_mul_overflow_p ();    /* { dg-error "not enough arguments to function" } */
   return x;
 }
 
@@ -15,6 +18,10 @@ f2 (int a, int b, int *c, int d)
   int x = __builtin_add_overflow (a, b, c, d); /* { dg-error "too many arguments to function" } */
   x += __builtin_sub_overflow (a, b, c, d, d, d);      /* { dg-error "too many arguments to function" } */
   x += __builtin_mul_overflow (a, b, c, d);    /* { dg-error "too many arguments to function" } */
+  x += __builtin_add_overflow_p (a, b, d, d);  /* { dg-error "too many arguments to function" } */
+  x += __builtin_sub_overflow_p (a, b, d, d, 1, d);    /* { dg-error "too many arguments to function" } */
+  x += __builtin_mul_overflow_p (a, b, d, d);  /* { dg-error "too many arguments to function" } */
+  
   return x;
 }
 
@@ -33,6 +40,15 @@ f3 (float fa, int a, _Complex long int ca, double fb, void *pb, int b, enum E eb
   x += __builtin_add_overflow (a, pb, c);      /* { dg-error "argument 2 in call to function\[^\n\r]*does not have integral type" } */
   x += __builtin_sub_overflow (a, eb, c);
   x += __builtin_mul_overflow (a, bb, c);
+  x += __builtin_add_overflow_p (fa, b, a);    /* { dg-error "argument 1 in call to function\[^\n\r]*does not have integral type" } */
+  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" } */
   return x;
 }
 
diff --git a/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c b/gcc/testsuite/c-c++-common/builtin-arith-overflow-2.c
new file mode 100644 (file)
index 0000000..4cbceff
--- /dev/null
@@ -0,0 +1,493 @@
+/* PR c/68120 - can't easily deal with integer overflow at compile time */
+/* { dg-do run } */
+/* { dg-additional-options "-Wno-long-long" } */
+
+#define SCHAR_MAX    __SCHAR_MAX__
+#define SHRT_MAX     __SHRT_MAX__
+#define INT_MAX             __INT_MAX__
+#define LONG_MAX     __LONG_MAX__
+#define LLONG_MAX    __LONG_LONG_MAX__
+       
+#define SCHAR_MIN    (-__SCHAR_MAX__ - 1)
+#define SHRT_MIN     (-__SHRT_MAX__ - 1)
+#define INT_MIN             (-__INT_MAX__ - 1)
+#define LONG_MIN     (-__LONG_MAX__ - 1)
+#define LLONG_MIN    (-__LONG_LONG_MAX__ - 1)
+       
+#define UCHAR_MAX    (SCHAR_MAX * 2U + 1)
+#define USHRT_MAX    (SHRT_MAX * 2U + 1)
+#define UINT_MAX     (INT_MAX * 2U + 1)
+#define ULONG_MAX    (LONG_MAX * 2LU + 1)
+#define ULLONG_MAX   (LLONG_MAX * 2LLU + 1)
+       
+#define USCHAR_MIN   (-__USCHAR_MAX__ - 1)
+#define USHRT_MIN    (-__USHRT_MAX__ - 1)
+#define UINT_MIN     (-__UINT_MAX__ - 1)
+#define ULONG_MIN    (-__ULONG_MAX__ - 1)
+#define ULLONG_MIN   (-__ULONG_LONG_MAX__ - 1)
+
+/* Number of failed runtime assertions.  */
+int nfails;
+
+void __attribute__ ((noclone, noinline))
+runtime_assert (int expr, int line)
+{
+  if (!expr)
+    {
+      __builtin_printf ("line %i: assertion failed\n", line);
+      ++nfails;
+    }
+}
+
+/* Helper macros for run-time testing.  */
+#define add(x, y) ((x) + (y))
+#define sadd(x, y) ((x) + (y))
+#define saddl(x, y) ((x) + (y))
+#define saddll(x, y) ((x) + (y))
+#define uadd(x, y) ((x) + (y))
+#define uaddl(x, y) ((x) + (y))
+#define uaddll(x, y) ((x) + (y))
+#define sub(x, y) ((x) - (y))
+#define ssub(x, y) ((x) - (y))
+#define ssubl(x, y) ((x) - (y))
+#define ssubll(x, y) ((x) - (y))
+#define usub(x, y) ((x) - (y))
+#define usubl(x, y) ((x) - (y))
+#define usubll(x, y) ((x) - (y))
+#define mul(x, y) ((x) * (y))
+#define smul(x, y) ((x) * (y))
+#define smull(x, y) ((x) * (y))
+#define smulll(x, y) ((x) * (y))
+#define umul(x, y) ((x) * (y))
+#define umull(x, y) ((x) * (y))
+#define umulll(x, y) ((x) * (y))
+
+int main (void)
+{
+
+#if __cplusplus >= 201103L
+#  define StaticAssert(expr) static_assert ((expr), #expr)
+#elif __STDC_VERSION__ >= 201112L
+#  define StaticAssert(expr) _Static_assert ((expr), #expr)
+#else
+  /* The following pragma has no effect due to bug 70888 - #pragma
+     diagnostic ignored -Wlong-long ineffective with __LONG_LONG_MAX__
+     in c++98 mode.  */
+#  pragma GCC diagnostic ignored "-Wlong-long"
+#  pragma GCC diagnostic ignored "-Wunused-local-typedefs"
+
+#  define CONCAT(a, b)  a ## b
+#  define CAT(a, b)     CONCAT (a, b)
+#  define StaticAssert(expr)                                   \
+     typedef int CAT (StaticAssert_, __LINE__) [1 - 2 * !(expr)]
+#endif
+
+  /* Make extra effort to prevent constant folding seeing the constant
+     values of the arguments and optimizing the run-time test into
+     a constant.  */
+#define RuntimeAssert(op, T, U, x, y, vflow)                           \
+  do {                                                                 \
+    volatile T a = (x), b = (y);                                       \
+    U c = 0;                                                           \
+    volatile int vf = __builtin_ ## op ## _overflow (a, b, &c);                \
+    runtime_assert ((vf == vflow), __LINE__);                          \
+    if (vf == 0)                                                       \
+      runtime_assert (op (a, b) == c, __LINE__);                       \
+  } while (0)
+
+  /* Verify that each call to the type-generic __builtin_op_overflow(x, y)
+     yields a constant expression equal to z indicating whether or not
+     the constant expression (x op y) overflows when evaluated in type T.  */
+#  define G_TEST(op, T, x, y, vflow)                                   \
+  RuntimeAssert(op, __typeof__ (op (x, y)), T, x, y, vflow);           \
+  StaticAssert ((vflow) == __builtin_ ## op ## _overflow_p ((x), (y), (T)0))
+
+  /* Addition.  */
+  G_TEST (add, signed char,    0,         0,         0);
+  G_TEST (add, signed char,    0,         SCHAR_MAX, 0);
+  G_TEST (add, signed char,    1,         SCHAR_MAX, 1);
+  G_TEST (add, signed char,    SCHAR_MAX, SCHAR_MAX, 1);
+  G_TEST (add, signed char,    0,         SCHAR_MIN, 0);
+  G_TEST (add, signed char,   -1,         SCHAR_MIN, 1);
+  /* Verify any slicing in the result type doesn't prevent the overflow
+     from being detected.  */
+  G_TEST (add, signed char,    UCHAR_MAX + 1, 0,     1);
+  G_TEST (add, signed char,    UCHAR_MAX + 1, 1,     1);
+  G_TEST (add, signed char,    1, UCHAR_MAX + 1,     1);
+
+  G_TEST (add, unsigned char,  0,        0,          0);
+  /* Verify any slicing in the result type doesn't prevent the overflow
+     from being detected.  */
+  G_TEST (add, unsigned char,  UCHAR_MAX + 1, 0,     1);
+  G_TEST (add, unsigned char,  UCHAR_MAX + 1, 1,     1);
+  G_TEST (add, unsigned char,  1, UCHAR_MAX + 1,     1);
+
+  G_TEST (add, short,          0,         0,         0);
+  G_TEST (add, short,          0,         SHRT_MAX,  0);
+  G_TEST (add, short,          1,         SHRT_MAX,  1);
+  G_TEST (add, short,          SHRT_MAX,  SHRT_MAX,  1);
+  G_TEST (add, short,          0,         SHRT_MIN,  0);
+  G_TEST (add, short,         -1,         SHRT_MIN,  1);
+  G_TEST (add, short,          SHRT_MIN,  SHRT_MIN,  1);
+
+  G_TEST (add, int,            0,         0,         0);
+  G_TEST (add, int,            0,         INT_MAX,   0);
+  G_TEST (add, int,            1,         INT_MAX,   1);
+  G_TEST (add, int,            INT_MAX,   INT_MAX,   1);
+  G_TEST (add, int,            0,         INT_MIN,   0);
+  G_TEST (add, int,           -1,         INT_MIN,   1);
+  G_TEST (add, int,            INT_MIN,   INT_MIN,   1);
+
+  G_TEST (add, long,           0,         0,         0);
+  G_TEST (add, long,           0,         LONG_MAX,  0);
+  G_TEST (add, long,           1,         LONG_MAX,  1);
+  G_TEST (add, long,           LONG_MAX,  LONG_MAX,  1);
+  G_TEST (add, long,           0,         LONG_MIN,  0);
+  G_TEST (add, long,          -1,         LONG_MIN,  1);
+  G_TEST (add, long,           LONG_MIN,  LONG_MIN,  1);
+
+  G_TEST (add, long long,      0,         0,          0);
+  G_TEST (add, long long,      0,         LLONG_MAX,  0);
+  G_TEST (add, long long,      1,         LLONG_MAX,  1);
+  G_TEST (add, long long,      LLONG_MAX, LLONG_MAX,  1);
+  G_TEST (add, long long,      0,         LLONG_MIN,  0);
+  G_TEST (add, long long,     -1,         LLONG_MIN,  1);
+  G_TEST (add, long long,      LLONG_MIN, LLONG_MIN,  1);
+
+  /* Subtraction */
+  G_TEST (sub, unsigned char,  0,         0,          0);
+  G_TEST (sub, unsigned char,  0,         UCHAR_MAX,  1);
+  G_TEST (sub, unsigned char,  1,         UCHAR_MAX,  1);
+
+  G_TEST (sub, unsigned char,  UCHAR_MAX, UCHAR_MAX,  0);
+  G_TEST (sub, unsigned short, 0,         0,          0);
+  G_TEST (sub, unsigned short, 0,         USHRT_MAX,  1);
+  G_TEST (sub, unsigned short, 1,         USHRT_MAX,  1);
+  G_TEST (sub, unsigned short, USHRT_MAX, USHRT_MAX,  0);
+
+  G_TEST (sub, unsigned,       0,         0,          0);
+  G_TEST (sub, unsigned,       0,         UINT_MAX,   1);
+  G_TEST (sub, unsigned,       1,         UINT_MAX,   1);
+  G_TEST (sub, unsigned,       UINT_MAX,  UINT_MAX,   0);
+
+  G_TEST (sub, unsigned long,  0,         0,          0);
+  G_TEST (sub, unsigned long,  0,         ULONG_MAX,  1);
+  G_TEST (sub, unsigned long,  1,         ULONG_MAX,  1);
+  G_TEST (sub, unsigned long,  ULONG_MAX, ULONG_MAX,  0);
+
+  G_TEST (sub, unsigned long long,  0,          0,          0);
+  G_TEST (sub, unsigned long long,  0,          ULLONG_MAX, 1);
+  G_TEST (sub, unsigned long long,  1,          ULLONG_MAX, 1);
+  G_TEST (sub, unsigned long long,  ULLONG_MAX, ULLONG_MAX, 0);
+
+  G_TEST (sub, signed char,    0,         0,           0);
+  G_TEST (sub, signed char,    0,         SCHAR_MAX,   0);
+  G_TEST (sub, signed char,    1,         SCHAR_MAX,   0);
+  G_TEST (sub, signed char,    SCHAR_MAX, SCHAR_MAX,   0);
+  G_TEST (sub, signed char,    SCHAR_MIN,         1,   1);
+  G_TEST (sub, signed char,    0,         SCHAR_MIN,   1);
+  G_TEST (sub, signed char,   -1,         SCHAR_MIN,   0);
+
+  G_TEST (sub, short,          0,         0,           0);
+  G_TEST (sub, short,          0,         SHRT_MAX,    0);
+  G_TEST (sub, short,          1,         SHRT_MAX,    0);
+  G_TEST (sub, short,          SHRT_MAX,  SHRT_MAX,    0);
+  G_TEST (sub, short,          0,         SHRT_MIN,    1);
+  G_TEST (sub, short,         -1,         SHRT_MIN,    0);
+  G_TEST (sub, short,          SHRT_MIN,  SHRT_MIN,    0);
+
+  G_TEST (sub, int,            0,         0,           0);
+  G_TEST (sub, int,            0,         INT_MAX,     0);
+  G_TEST (sub, int,            1,         INT_MAX,     0);
+  G_TEST (sub, int,            INT_MAX,   INT_MAX,     0);
+  G_TEST (sub, int,            0,         INT_MIN,     1);
+  G_TEST (sub, int,           -1,         INT_MIN,     0);
+  G_TEST (sub, int,            INT_MIN,   INT_MIN,     0);
+
+  G_TEST (sub, long,           0,         0,           0);
+  G_TEST (sub, long,           0,         LONG_MAX,    0);
+  G_TEST (sub, long,           1,         LONG_MAX,    0);
+  G_TEST (sub, long,           LONG_MAX,  LONG_MAX,    0);
+  G_TEST (sub, long,           0,         LONG_MIN,    1);
+  G_TEST (sub, long,          -1,         LONG_MIN,    0);
+  G_TEST (sub, long,           LONG_MIN,  LONG_MIN,    0);
+
+  G_TEST (sub, long long,      0,           0,           0);
+  G_TEST (sub, long long,      0,           LLONG_MAX,   0);
+  G_TEST (sub, long long,      1,           LLONG_MAX,   0);
+  G_TEST (sub, long long,      LLONG_MAX,   LLONG_MAX,   0);
+  G_TEST (sub, long long,      0,           LLONG_MIN,   1);
+  G_TEST (sub, long long,     -1,           LLONG_MIN,   0);
+  G_TEST (sub, long long,      LLONG_MIN,   LLONG_MIN,   0);
+
+  G_TEST (sub, unsigned char,  0,         0,          0);
+  G_TEST (sub, unsigned char,  0,         UCHAR_MAX,  1);
+  G_TEST (sub, unsigned char,  1,         UCHAR_MAX,  1);
+  G_TEST (sub, unsigned char,  UCHAR_MAX, UCHAR_MAX,  0);
+
+  G_TEST (sub, unsigned short, 0,         0,          0);
+  G_TEST (sub, unsigned short, 0,         USHRT_MAX,  1);
+  G_TEST (sub, unsigned short, 1,         USHRT_MAX,  1);
+  G_TEST (sub, unsigned short, USHRT_MAX, USHRT_MAX,  0);
+
+  G_TEST (sub, unsigned,       0,         0,          0);
+  G_TEST (sub, unsigned,       0,         UINT_MAX,   1);
+  G_TEST (sub, unsigned,       1,         UINT_MAX,   1);
+  G_TEST (sub, unsigned,       UINT_MAX,  UINT_MAX,   0);
+
+  G_TEST (sub, unsigned long,  0,         0,          0);
+  G_TEST (sub, unsigned long,  0,         ULONG_MAX,  1);
+  G_TEST (sub, unsigned long,  1,         ULONG_MAX,  1);
+  G_TEST (sub, unsigned long,  ULONG_MAX, ULONG_MAX,  0);
+
+  G_TEST (sub, unsigned long long,  0,          0,          0);
+  G_TEST (sub, unsigned long long,  0,          ULLONG_MAX, 1);
+  G_TEST (sub, unsigned long long,  1,          ULLONG_MAX, 1);
+  G_TEST (sub, unsigned long long,  ULLONG_MAX, ULLONG_MAX, 0);
+
+  /* Multiplication.  */
+  G_TEST (mul, unsigned char,  0,         0,          0);
+  G_TEST (mul, unsigned char,  0,         UCHAR_MAX,  0);
+  G_TEST (mul, unsigned char,  1,         UCHAR_MAX,  0);
+  G_TEST (mul, unsigned char,  2,         UCHAR_MAX,  1);
+  G_TEST (mul, unsigned char,  UCHAR_MAX, UCHAR_MAX,  1);
+
+  G_TEST (mul, unsigned short, 0,         0,          0);
+  G_TEST (mul, unsigned short, 0,         USHRT_MAX,  0);
+  G_TEST (mul, unsigned short, 1,         USHRT_MAX,  0);
+  G_TEST (mul, unsigned short, USHRT_MAX, 2,          1);
+  G_TEST (mul, unsigned short, USHRT_MAX, USHRT_MAX,  1);
+
+  G_TEST (mul, unsigned,       0,         0,          0);
+  G_TEST (mul, unsigned,       0,         UINT_MAX,   0);
+  G_TEST (mul, unsigned,       1,         UINT_MAX,   0);
+  G_TEST (mul, unsigned,       2,         UINT_MAX,   1);
+  G_TEST (mul, unsigned,       UINT_MAX,  UINT_MAX,   1);
+
+  G_TEST (mul, unsigned long,  0,         0,          0);
+  G_TEST (mul, unsigned long,  0,         ULONG_MAX,  0);
+  G_TEST (mul, unsigned long,  1,         ULONG_MAX,  0);
+  G_TEST (mul, unsigned long,  2,         ULONG_MAX,  1);
+  G_TEST (mul, unsigned long,  ULONG_MAX, ULONG_MAX,  1);
+
+  G_TEST (mul, unsigned long long,  0,          0,          0);
+  G_TEST (mul, unsigned long long,  0,          ULLONG_MAX, 0);
+  G_TEST (mul, unsigned long long,  1,          ULLONG_MAX, 0);
+  G_TEST (mul, unsigned long long,  2,          ULLONG_MAX, 1);
+  G_TEST (mul, unsigned long long,  ULLONG_MAX, ULLONG_MAX, 1);
+
+  G_TEST (mul, signed char,  0,         0,           0);
+  G_TEST (mul, signed char,  0,         SCHAR_MAX,   0);
+  G_TEST (mul, signed char,  1,         SCHAR_MAX,   0);
+  G_TEST (mul, signed char,  SCHAR_MAX, SCHAR_MAX,   1);
+  G_TEST (mul, signed char,  SCHAR_MIN,         1,   0);
+  G_TEST (mul, signed char,  0,         SCHAR_MIN,   0);
+  G_TEST (mul, signed char, -1,         SCHAR_MIN,   1);
+
+  G_TEST (mul, short,        0,         0,           0);
+  G_TEST (mul, short,        0,         SHRT_MAX,    0);
+  G_TEST (mul, short,        1,         SHRT_MAX,    0);
+  G_TEST (mul, short,        SHRT_MAX,  SHRT_MAX,    1);
+  G_TEST (mul, short,        0,         SHRT_MIN,    0);
+  G_TEST (mul, short,       -1,         SHRT_MIN,    1);
+  G_TEST (mul, short,        SHRT_MIN,  SHRT_MIN,    1);
+
+  G_TEST (mul, int,          0,         0,           0);
+  G_TEST (mul, int,          0,         INT_MAX,     0);
+  G_TEST (mul, int,          1,         INT_MAX,     0);
+  G_TEST (mul, int,          INT_MAX,   INT_MAX,     1);
+  G_TEST (mul, int,          0,         INT_MIN,     0);
+  G_TEST (mul, int,         -1,         INT_MIN,     1);
+  G_TEST (mul, int,          INT_MIN,   INT_MIN,     1);
+
+  G_TEST (mul, long,         0,         0,           0);
+  G_TEST (mul, long,         0,         LONG_MAX,    0);
+  G_TEST (mul, long,         1,         LONG_MAX,    0);
+  G_TEST (mul, long,         LONG_MAX,  LONG_MAX,    1);
+  G_TEST (mul, long,         0,         LONG_MIN,    0);
+  G_TEST (mul, long,        -1,         LONG_MIN,    1);
+  G_TEST (mul, long,         LONG_MIN,  LONG_MIN,    1);
+
+  G_TEST (mul, long long,    0,           0,           0);
+  G_TEST (mul, long long,    0,           LLONG_MAX,   0);
+  G_TEST (mul, long long,    1,           LLONG_MAX,   0);
+  G_TEST (mul, long long,    LLONG_MAX,   LLONG_MAX,   1);
+  G_TEST (mul, long long,    0,           LLONG_MIN,   0);
+  G_TEST (mul, long long,   -1,           LLONG_MIN,   1);
+  G_TEST (mul, long long,    LLONG_MIN,   LLONG_MIN,   1);
+
+  G_TEST (mul, unsigned char,  0,         0,          0);
+  G_TEST (mul, unsigned char,  0,         UCHAR_MAX,  0);
+  G_TEST (mul, unsigned char,  1,         UCHAR_MAX,  0);
+  G_TEST (mul, unsigned char,  UCHAR_MAX, UCHAR_MAX,  1);
+
+  G_TEST (mul, unsigned short, 0,         0,          0);
+  G_TEST (mul, unsigned short, 0,         USHRT_MAX,  0);
+  G_TEST (mul, unsigned short, 1,         USHRT_MAX,  0);
+  G_TEST (mul, unsigned short, USHRT_MAX, USHRT_MAX,  1);
+
+  G_TEST (mul, unsigned,       0,         0,          0);
+  G_TEST (mul, unsigned,       0,         UINT_MAX,   0);
+  G_TEST (mul, unsigned,       1,         UINT_MAX,   0);
+  G_TEST (mul, unsigned,       UINT_MAX,  UINT_MAX,   1);
+
+  G_TEST (mul, unsigned long,  0,         0,          0);
+  G_TEST (mul, unsigned long,  0,         ULONG_MAX,  0);
+  G_TEST (mul, unsigned long,  1,         ULONG_MAX,  0);
+  G_TEST (mul, unsigned long,  ULONG_MAX, ULONG_MAX,  1);
+
+  G_TEST (mul, unsigned long long,  0,          0,          0);
+  G_TEST (mul, unsigned long long,  0,          ULLONG_MAX, 0);
+  G_TEST (mul, unsigned long long,  1,          ULLONG_MAX, 0);
+  G_TEST (mul, unsigned long long,  ULLONG_MAX, ULLONG_MAX, 1);
+
+  /* Verify that each call to the type-specific __builtin_op_overflow
+     evaluates to a (not-necessarily constant) expression indicating
+     whether or not the constant expression (x op y) overflows.
+     The type-specific forms of the built-ins detect overflow after
+     arithmetic promotions and so unlike the type-generic overloads
+     cannot detect overflow in char or short types.  */
+
+#define T_TEST(op, T, x, y, vflow)                             \
+  RuntimeAssert (op, T, __typeof__ ((x) + (y)), x, y, vflow)
+
+  /* Signed int addition.  */
+  T_TEST (sadd,   signed char,    0,         0,         0);
+  T_TEST (sadd,   signed char,    0,         SCHAR_MAX, 0);
+  T_TEST (sadd,   signed char,    1,         SCHAR_MAX, 0);
+  T_TEST (sadd,   signed char,    SCHAR_MAX, SCHAR_MAX, 0);
+  T_TEST (sadd,   signed char,    0,         SCHAR_MIN, 0);
+  T_TEST (sadd,   signed char,   -1,         SCHAR_MIN, 0);
+
+  T_TEST (sadd,   short,          0,         0,         0);
+  T_TEST (sadd,   short,          0,         SHRT_MAX,  0);
+  T_TEST (sadd,   short,          1,         SHRT_MAX,  0);
+  T_TEST (sadd,   short,          SHRT_MAX,  SHRT_MAX,  0);
+  T_TEST (sadd,   short,          0,         SHRT_MIN,  0);
+  T_TEST (sadd,   short,         -1,         SHRT_MIN,  0);
+  T_TEST (sadd,   short,          SHRT_MIN,  SHRT_MIN,  0);
+
+  T_TEST (sadd,   int,            0,         0,         0);
+  T_TEST (sadd,   int,            0,         INT_MAX,   0);
+  T_TEST (sadd,   int,            1,         INT_MAX,   1);
+  T_TEST (sadd,   int,            INT_MAX,   INT_MAX,   1);
+  T_TEST (sadd,   int,            0,         INT_MIN,   0);
+  T_TEST (sadd,   int,           -1,         INT_MIN,   1);
+  T_TEST (sadd,   int,            INT_MIN,   INT_MIN,   1);
+
+  /* Signed long addition.  */
+  T_TEST (saddl,  long,           0L,        0L,        0);
+  T_TEST (saddl,  long,           0L,        LONG_MAX,  0);
+  T_TEST (saddl,  long,           1L,        LONG_MAX,  1);
+  T_TEST (saddl,  long,           LONG_MAX,  LONG_MAX,  1);
+  T_TEST (saddl,  long,           0L,        LONG_MIN,  0);
+  T_TEST (saddl,  long,          -1L,        LONG_MIN,  1);
+  T_TEST (saddl,  long,           LONG_MIN,  LONG_MIN,  1);
+
+  T_TEST (saddll, long long,      0LL,       0LL,        0);
+  T_TEST (saddll, long long,      0LL,       LLONG_MAX,  0);
+  T_TEST (saddll, long long,      1LL,       LLONG_MAX,  1);
+  T_TEST (saddll, long long,      LLONG_MAX, LLONG_MAX,  1);
+  T_TEST (saddll, long long,      0LL,       LLONG_MIN,  0);
+  T_TEST (saddll, long long,     -1LL,       LLONG_MIN,  1);
+  T_TEST (saddll, long long,      LLONG_MIN, LLONG_MIN,  1);
+
+  /* Unsigned int addition.  */
+  T_TEST (uadd,   unsigned char,  0U,        0U,         0);
+  T_TEST (uadd,   unsigned char,  0U,        UCHAR_MAX, 0);
+  T_TEST (uadd,   unsigned char,  1U,        UCHAR_MAX, 0);
+  T_TEST (uadd,   unsigned char,  UCHAR_MAX, UCHAR_MAX, 0);
+
+  T_TEST (uadd,   unsigned short, 0U,        0U,         0);
+  T_TEST (uadd,   unsigned short, 0U,        USHRT_MAX,  0);
+  T_TEST (uadd,   unsigned short, 1U,        USHRT_MAX,  0);
+  T_TEST (uadd,   unsigned short, USHRT_MAX, USHRT_MAX,  0);
+
+  T_TEST (uadd,   unsigned,       0U,        0U,         0);
+  T_TEST (uadd,   unsigned,       0U,        UINT_MAX,   0);
+  T_TEST (uadd,   unsigned,       1U,        UINT_MAX,   1);
+  T_TEST (uadd,   unsigned,       UINT_MAX,  UINT_MAX,   1);
+
+  /* Unsigned long addition.  */
+  T_TEST (uaddl,  unsigned long,  0UL,       0UL,       0);
+  T_TEST (uaddl,  unsigned long,  0UL,       ULONG_MAX, 0);
+  T_TEST (uaddl,  unsigned long,  1UL,       ULONG_MAX, 1);
+  T_TEST (uaddl,  unsigned long,  ULONG_MAX, ULONG_MAX, 1);
+
+  T_TEST (uaddll, unsigned long long, 0ULL,       0ULL,       0);
+  T_TEST (uaddll, unsigned long long, 0ULL,       ULLONG_MAX, 0);
+  T_TEST (uaddll, unsigned long long, 1ULL,       ULLONG_MAX, 1);
+  T_TEST (uaddll, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1);
+
+  /* Signed int subtraction.  */
+  T_TEST (ssub,   signed char,    0,         0,          0);
+  T_TEST (ssub,   signed char,    0,         SCHAR_MAX,  0);
+  T_TEST (ssub,   signed char,    1,         SCHAR_MAX,  0);
+  T_TEST (ssub,   signed char,    SCHAR_MAX, SCHAR_MAX,  0);
+  T_TEST (ssub,   signed char,    0,         SCHAR_MIN,  0);
+  T_TEST (ssub,   signed char,   -1,         SCHAR_MIN,  0);
+
+  T_TEST (ssub,   short,          0,         0,          0);
+  T_TEST (ssub,   short,          0,         SHRT_MAX,   0);
+  T_TEST (ssub,   short,          1,         SHRT_MAX,   0);
+  T_TEST (ssub,   short,          SHRT_MAX,  SHRT_MAX,   0);
+  T_TEST (ssub,   short,          0,         SHRT_MIN,   0);
+  T_TEST (ssub,   short,         -1,         SHRT_MIN,   0);
+  T_TEST (ssub,   short,          SHRT_MIN,  SHRT_MIN,   0);
+
+  T_TEST (ssub,   int,            0,         0,          0);
+  T_TEST (ssub,   int,            0,         INT_MAX,    0);
+  T_TEST (ssub,   int,            1,         INT_MAX,    0);
+  T_TEST (ssub,   int,            INT_MAX,   INT_MAX,    0);
+  T_TEST (ssub,   int,            0,         INT_MIN,    1);
+  T_TEST (ssub,   int,           -1,         INT_MIN,    0);
+  T_TEST (ssub,   int,            INT_MIN,   INT_MIN,    0);
+
+  /* Signed long subtraction.  */
+  T_TEST (ssubl,  long,           0L,        0L,         0);
+  T_TEST (ssubl,  long,           0L,        LONG_MAX,   0);
+  T_TEST (ssubl,  long,           1L,        LONG_MAX,   0);
+  T_TEST (ssubl,  long,           LONG_MAX,  LONG_MAX,   0);
+  T_TEST (ssubl,  long,           0L,        LONG_MIN,   1);
+  T_TEST (ssubl,  long,          -1L,        LONG_MIN,   0);
+  T_TEST (ssubl,  long,           LONG_MIN,  LONG_MIN,   0);
+
+  /* Signed long long subtraction.  */
+  T_TEST (ssubll, long long,      0LL,       0LL,        0);
+  T_TEST (ssubll, long long,      0LL,       LLONG_MAX,  0);
+  T_TEST (ssubll, long long,      1LL,       LLONG_MAX,  0);
+  T_TEST (ssubll, long long,      LLONG_MAX, LLONG_MAX,  0);
+  T_TEST (ssubll, long long,      0LL,       LLONG_MIN,  1);
+  T_TEST (ssubll, long long,     -1LL,       LLONG_MIN,  0);
+  T_TEST (ssubll, long long,      LLONG_MIN, LLONG_MIN,  0);
+
+  /* Unsigned int subtraction.  */
+  T_TEST (usub,   unsigned char,  0U,        0U,         0);
+  T_TEST (usub,   unsigned char,  0U,        UCHAR_MAX,  1);
+  T_TEST (usub,   unsigned char,  1U,        UCHAR_MAX,  1);
+  T_TEST (usub,   unsigned char,  UCHAR_MAX, UCHAR_MAX,  0);
+
+  T_TEST (usub,   unsigned short, 0U,        0U,         0);
+  T_TEST (usub,   unsigned short, 0U,        USHRT_MAX,  1);
+  T_TEST (usub,   unsigned short, 1U,        USHRT_MAX,  1);
+  T_TEST (usub,   unsigned short, USHRT_MAX, USHRT_MAX,  0);
+
+  T_TEST (usub,   unsigned,       0U,        0U,         0);
+  T_TEST (usub,   unsigned,       0U,        UINT_MAX,   1);
+  T_TEST (usub,   unsigned,       1U,        UINT_MAX,   1);
+  T_TEST (usub,   unsigned,       UINT_MAX,  UINT_MAX,   0);
+
+  /* Unsigned long subtraction.  */
+  T_TEST (usubl,  unsigned long,  0UL,       0UL,       0);
+  T_TEST (usubl,  unsigned long,  0UL,       ULONG_MAX, 1);
+  T_TEST (usubl,  unsigned long,  1UL,       ULONG_MAX, 1);
+  T_TEST (usubl,  unsigned long,  ULONG_MAX, ULONG_MAX, 0);
+
+  /* Unsigned long long subtraction.  */
+  T_TEST (usubll, unsigned long long,  0ULL,       0ULL,       0);
+  T_TEST (usubll, unsigned long long,  0ULL,       ULLONG_MAX, 1);
+  T_TEST (usubll, unsigned long long,  1ULL,       ULLONG_MAX, 1);
+  T_TEST (usubll, unsigned long long,  ULLONG_MAX, ULLONG_MAX, 0);
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C
new file mode 100644 (file)
index 0000000..a63c0a1
--- /dev/null
@@ -0,0 +1,212 @@
+// PR c++/70507 - integer overflow builtins not constant expressions
+// { dg-do compile { target c++11 } }
+
+#define SCHAR_MAX    __SCHAR_MAX__
+#define SHRT_MAX     __SHRT_MAX__
+#define INT_MAX             __INT_MAX__
+#define LONG_MAX     __LONG_MAX__
+#define LLONG_MAX    __LONG_LONG_MAX__
+       
+#define SCHAR_MIN    (-__SCHAR_MAX__ - 1)
+#define SHRT_MIN     (-__SHRT_MAX__ - 1)
+#define INT_MIN             (-__INT_MAX__ - 1)
+#define LONG_MIN     (-__LONG_MAX__ - 1)
+#define LLONG_MIN    (-__LONG_LONG_MAX__ - 1)
+       
+#define UCHAR_MAX    (SCHAR_MAX * 2U + 1)
+#define USHRT_MAX    (SHRT_MAX * 2U + 1)
+#define UINT_MAX     (INT_MAX * 2U + 1)
+#define ULONG_MAX    (LONG_MAX * 2LU + 1)
+#define ULLONG_MAX   (LLONG_MAX * 2LLU + 1)
+       
+#define USCHAR_MIN   (-__USCHAR_MAX__ - 1)
+#define USHRT_MIN    (-__USHRT_MAX__ - 1)
+#define UINT_MIN     (-__UINT_MAX__ - 1)
+#define ULONG_MIN    (-__ULONG_MAX__ - 1)
+#define ULLONG_MIN   (-__ULONG_LONG_MAX__ - 1)
+
+#define Assert(expr) static_assert ((expr), #expr)
+
+template <class T>
+constexpr T add (T x, T y, T z = T ())
+{
+  return __builtin_add_overflow (x, y, &z) ? 0 : z;
+}
+
+template <class T>
+constexpr T sub (T x, T y, T z = T ())
+{
+  return __builtin_sub_overflow (x, y, &z) ? 0 : z;
+}
+
+template <class T>
+constexpr T mul (T x, T y, T z = T ())
+{
+  return __builtin_mul_overflow (x, y, &z) ? 0 : z;
+}
+
+#define TEST_ADD(T, x, y, z) Assert (z == add<T>(x, y))
+#define TEST_SUB(T, x, y, z) Assert (z == sub<T>(x, y))
+#define TEST_MUL(T, x, y, z) Assert (z == mul<T>(x, y))
+
+
+TEST_ADD (signed char,  0,         0,         0);
+TEST_ADD (signed char,  0,         SCHAR_MAX, SCHAR_MAX);
+TEST_ADD (signed char,  1,         SCHAR_MAX, 0);           // overflow
+TEST_ADD (signed char,  SCHAR_MAX, SCHAR_MAX, 0);           // overflow
+TEST_ADD (signed char,  0,         SCHAR_MIN, SCHAR_MIN);
+TEST_ADD (signed char, -1,         SCHAR_MIN, 0);           // overflow
+
+TEST_ADD (short,        0,         0,         0);
+TEST_ADD (short,        0,         SHRT_MAX,  SHRT_MAX);
+TEST_ADD (short,        1,         SHRT_MAX,  0);           // overflow
+TEST_ADD (short,        SHRT_MAX,  SHRT_MAX,  0);           // overflow
+TEST_ADD (short,        0,         SHRT_MIN,  SHRT_MIN);
+TEST_ADD (short,       -1,         SHRT_MIN,  0);           // overflow
+TEST_ADD (short,        SHRT_MIN,  SHRT_MIN,  0);           // overflow
+
+TEST_ADD (int,          0,         0,         0);
+TEST_ADD (int,          0,         INT_MAX,   INT_MAX);
+TEST_ADD (int,          1,         INT_MAX,   0);           // overflow
+TEST_ADD (int,          INT_MAX,   INT_MAX,   0);           // overflow
+TEST_ADD (int,          0,         INT_MIN,   INT_MIN);
+TEST_ADD (int,         -1,         INT_MIN,   0);           // overflow
+TEST_ADD (int,          INT_MIN,   INT_MIN,   0);           // overflow
+
+TEST_ADD (long,         0,         0,         0);
+TEST_ADD (long,         0,         LONG_MAX,  LONG_MAX);
+TEST_ADD (long,         1,         LONG_MAX,  0);           // overflow
+TEST_ADD (long,         LONG_MAX,  LONG_MAX,  0);           // overflow
+TEST_ADD (long,         0,         LONG_MIN,  LONG_MIN);
+TEST_ADD (long,        -1,         LONG_MIN,  0);           // overflow
+TEST_ADD (long,         LONG_MIN,  LONG_MIN,  0);           // overflow
+
+TEST_ADD (long long,    0,         0,          0);
+TEST_ADD (long long,    0,         LLONG_MAX,  LLONG_MAX);
+TEST_ADD (long long,    1,         LLONG_MAX,  0);          // overflow
+TEST_ADD (long long,    LLONG_MAX, LLONG_MAX,  0);          // overflow
+TEST_ADD (long long,    0,         LLONG_MIN,  LLONG_MIN);
+TEST_ADD (long long,   -1,         LLONG_MIN,  0);          // overflow
+TEST_ADD (long long,    LLONG_MIN, LLONG_MIN,  0);          // overflow
+
+TEST_ADD (unsigned char,  0,         0,         0);
+TEST_ADD (unsigned char,  0,         UCHAR_MAX, UCHAR_MAX);
+TEST_ADD (unsigned char,  1,         UCHAR_MAX, 0);         // overflow
+
+TEST_ADD (unsigned char,  UCHAR_MAX, UCHAR_MAX, 0);         // overflow
+TEST_ADD (unsigned short, 0,         0,          0);
+TEST_ADD (unsigned short, 0,         USHRT_MAX,  USHRT_MAX);
+TEST_ADD (unsigned short, 1,         USHRT_MAX,  0);        // overflow
+TEST_ADD (unsigned short, USHRT_MAX, USHRT_MAX,  0);        // overflow
+
+TEST_ADD (unsigned,       0,         0,          0);
+TEST_ADD (unsigned,       0,         UINT_MAX,   UINT_MAX);
+TEST_ADD (unsigned,       1,         UINT_MAX,   0);        // overflow
+TEST_ADD (unsigned,       UINT_MAX,  UINT_MAX,   0);        // overflow
+
+TEST_ADD (unsigned long,  0,         0,         0);
+TEST_ADD (unsigned long,  0,         ULONG_MAX, ULONG_MAX);
+TEST_ADD (unsigned long,  1,         ULONG_MAX, 0);         // overflow
+TEST_ADD (unsigned long,  ULONG_MAX, ULONG_MAX, 0);         // overflow
+
+TEST_ADD (unsigned long long,  0,          0,          0);
+TEST_ADD (unsigned long long,  0,          ULLONG_MAX, ULLONG_MAX);
+TEST_ADD (unsigned long long,  1,          ULLONG_MAX, 0);  // overflow
+TEST_ADD (unsigned long long,  ULLONG_MAX, ULLONG_MAX, 0);  // overflow
+
+
+// Make sure the built-ins are accepted in the following contexts
+// where constant expressions are required and that they return
+// the expected overflow value.
+
+namespace Enum {
+
+enum Add {
+  a0 = __builtin_add_overflow_p (      1, 1, 0),
+  a1 = __builtin_add_overflow_p (INT_MAX, 1, 0)
+};
+
+Assert (a0 == 0);
+Assert (a1 == 1);
+
+enum Sub {
+  s0 = __builtin_sub_overflow_p (      1, 1, 0),
+  s1 = __builtin_sub_overflow_p (INT_MIN, 1, 0)
+};
+
+Assert (s0 == 0);
+Assert (s1 == 1);
+
+enum Mul {
+  m0 = __builtin_add_overflow_p (      1,       1, 0),
+  m1 = __builtin_add_overflow_p (INT_MAX, INT_MAX, 0)
+};
+
+Assert (m0 == 0);
+Assert (m1 == 1);
+
+}   // namespace Enum
+
+namespace TemplateArg {
+
+template <class T, class U, class V,
+         T x, U y, bool v, bool z = __builtin_add_overflow_p (x, y, V ())>
+struct Add {
+  Assert (z == v);
+};
+
+template <class T, class U, class V,
+         T x, U y, bool v, bool z = __builtin_sub_overflow_p (x, y, V ())>
+struct Sub {
+  Assert (z == v);
+};
+
+template <class T, class U, class V,
+         T x, U y, bool v, bool z = __builtin_mul_overflow_p (x, y, V ())>
+struct Mul {
+  Assert (z == v);
+};
+
+template struct Add<int, int, int,  1,       1, false>;
+template struct Add<int, int, int,  1, INT_MAX, true>;
+
+template struct Sub<int, int, int,  1,       1, false>;
+template struct Sub<int, int, int, -2, INT_MAX, true>;
+
+template struct Mul<int, int, int,  1,               1, false>;
+template struct Mul<int, int, int,  2, INT_MAX / 2 + 1, true>;
+
+}   // namespace TemplateArg
+
+#if __cplusplus >= 201402L
+
+namespace Initializer {
+
+struct Result {
+  int res;
+  bool vflow;
+};
+
+constexpr Result
+add_vflow (int a, int b)
+{
+#if 1
+  Result res = { a + b, __builtin_add_overflow_p (a, b, int ()) };
+#else
+  // The following fails to compile because of c++/71391 - error
+  // on aggregate initialization with side-effects in a constexpr
+  // function
+  int c = 0;
+  Result res = { 0, __builtin_add_overflow (a, b, &c) };
+  res.c = c;
+#endif
+  return res;
+}
+
+constexpr Result sum = add_vflow (123, 456);
+Assert (sum.res == 123 + 456);
+Assert (!sum.vflow);
+
+}   // namespace Initializer
+
+#endif   // __cplusplus >= 201402L
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-arith-overflow.C
new file mode 100644 (file)
index 0000000..7ca0033
--- /dev/null
@@ -0,0 +1,229 @@
+// Test to exercise that the type-specific integer arithmetic built-ins
+// with overflow checking can be used in C++ 14 constant expressions.
+// -Woverflow is disabled to prevent (bogus?) G++ warnings.
+// { dg-do compile { target c++14 } }
+// { dg-additional-options "-Wno-overflow" }
+
+#define SCHAR_MAX    __SCHAR_MAX__
+#define SHRT_MAX     __SHRT_MAX__
+#define INT_MAX             __INT_MAX__
+#define LONG_MAX     __LONG_MAX__
+#define LLONG_MAX    __LONG_LONG_MAX__
+       
+#define SCHAR_MIN    (-__SCHAR_MAX__ - 1)
+#define SHRT_MIN     (-__SHRT_MAX__ - 1)
+#define INT_MIN             (-__INT_MAX__ - 1)
+#define LONG_MIN     (-__LONG_MAX__ - 1)
+#define LLONG_MIN    (-__LONG_LONG_MAX__ - 1)
+       
+#define UCHAR_MAX    (SCHAR_MAX * 2U + 1)
+#define USHRT_MAX    (SHRT_MAX * 2U + 1)
+#define UINT_MAX     (INT_MAX * 2U + 1)
+#define ULONG_MAX    (LONG_MAX * 2LU + 1)
+#define ULLONG_MAX   (LLONG_MAX * 2LLU + 1)
+       
+#define USCHAR_MIN   (-__USCHAR_MAX__ - 1)
+#define USHRT_MIN    (-__USHRT_MAX__ - 1)
+#define UINT_MIN     (-__UINT_MAX__ - 1)
+#define ULONG_MIN    (-__ULONG_MAX__ - 1)
+#define ULLONG_MIN   (-__ULONG_LONG_MAX__ - 1)
+
+// Helper macros.
+#define sadd(x, y) ((x) + (y))
+#define saddl(x, y) ((x) + (y))
+#define saddll(x, y) ((x) + (y))
+#define uadd(x, y) ((x) + (y))
+#define uaddl(x, y) ((x) + (y))
+#define uaddll(x, y) ((x) + (y))
+#define ssub(x, y) ((x) - (y))
+#define ssubl(x, y) ((x) - (y))
+#define ssubll(x, y) ((x) - (y))
+#define usub(x, y) ((x) - (y))
+#define usubl(x, y) ((x) - (y))
+#define usubll(x, y) ((x) - (y))
+#define smul(x, y) ((x) * (y))
+#define smull(x, y) ((x) * (y))
+#define smulll(x, y) ((x) * (y))
+#define umul(x, y) ((x) * (y))
+#define umull(x, y) ((x) * (y))
+#define umulll(x, y) ((x) * (y))
+
+// Result object.
+template <class T>
+struct Res
+{
+  constexpr Res (T a, bool v): z (a), v (v) { }
+  T z; bool v;
+};
+
+template <class T>
+constexpr bool operator== (Res<T> a, Res<T> b)
+{
+  return a.z == b.z && a.v == b.v;
+}
+
+#define StaticAssert(expr) static_assert ((expr), #expr)
+
+#define CONCAT(a, b)   a ## b
+#define CAT(a, b)      CONCAT (a, b)
+
+// Helper to determine the type of the result of the arithmetic
+// as specified by the built-ins.
+template <class T> struct ResType { typedef T type; };
+template <>        struct ResType<signed char> { typedef int type; };
+template <>        struct ResType<unsigned char> { typedef unsigned type; };
+template <>        struct ResType<signed short> { typedef int type; };
+template <>        struct ResType<unsigned short> { typedef unsigned type; };
+
+// Macro to define a single test case verifying that integer overflow
+// is detected when expected, and when not, that the result matches
+// the result computed using ordinary arithmetic.  The result cannot
+// be tested in the presence of overflow since it's not a core
+// constant expression.
+#define TEST(op, T, x, y, vflow)                                       \
+  constexpr Res<T> CAT (op, __LINE__)(T a, T b)                                \
+  {                                                                    \
+    ResType<T>::type c = 0;                                            \
+    bool v = __builtin_ ## op ## _overflow (a, b, &c);                 \
+    return Res<T>(c, v);                                               \
+  }                                                                    \
+  StaticAssert (vflow ? CAT (op, __LINE__)(x, y).v                     \
+               : CAT (op, __LINE__)(x, y) == Res<T>(op (x, y), vflow))
+
+/* Signed int addition.  */
+TEST (sadd,   signed char,    0,         0,         0);
+TEST (sadd,   signed char,    0,         SCHAR_MAX, 0);
+TEST (sadd,   signed char,    1,         SCHAR_MAX, 0);
+TEST (sadd,   signed char,    SCHAR_MAX, SCHAR_MAX, 0);
+TEST (sadd,   signed char,    0,         SCHAR_MIN, 0);
+TEST (sadd,   signed char,   -1,         SCHAR_MIN, 0);
+
+TEST (sadd,   short,          0,         0,         0);
+TEST (sadd,   short,          0,         SHRT_MAX,  0);
+TEST (sadd,   short,          1,         SHRT_MAX,  0);
+TEST (sadd,   short,          SHRT_MAX,  SHRT_MAX,  0);
+TEST (sadd,   short,          0,         SHRT_MIN,  0);
+TEST (sadd,   short,         -1,         SHRT_MIN,  0);
+TEST (sadd,   short,          SHRT_MIN,  SHRT_MIN,  0);
+
+TEST (sadd,   int,            0,         0,         0);
+TEST (sadd,   int,            0,         INT_MAX,   0);
+TEST (sadd,   int,            1,         INT_MAX,   1);
+TEST (sadd,   int,            INT_MAX,   INT_MAX,   1);
+TEST (sadd,   int,            0,         INT_MIN,   0);
+TEST (sadd,   int,           -1,         INT_MIN,   1);
+TEST (sadd,   int,            INT_MIN,   INT_MIN,   1);
+
+/* Signed long addition.  */
+TEST (saddl,  long,           0L,        0L,        0);
+TEST (saddl,  long,           0L,        LONG_MAX,  0);
+TEST (saddl,  long,           1L,        LONG_MAX,  1);
+TEST (saddl,  long,           LONG_MAX,  LONG_MAX,  1);
+TEST (saddl,  long,           0L,        LONG_MIN,  0);
+TEST (saddl,  long,          -1L,        LONG_MIN,  1);
+TEST (saddl,  long,           LONG_MIN,  LONG_MIN,  1);
+
+TEST (saddll, long long,      0LL,       0LL,        0);
+TEST (saddll, long long,      0LL,       LLONG_MAX,  0);
+TEST (saddll, long long,      1LL,       LLONG_MAX,  1);
+TEST (saddll, long long,      LLONG_MAX, LLONG_MAX,  1);
+TEST (saddll, long long,      0LL,       LLONG_MIN,  0);
+TEST (saddll, long long,     -1LL,       LLONG_MIN,  1);
+TEST (saddll, long long,      LLONG_MIN, LLONG_MIN,  1);
+
+/* Unsigned int addition.  */
+TEST (uadd,   unsigned char,  0U,        0U,         0);
+TEST (uadd,   unsigned char,  0U,        UCHAR_MAX, 0);
+TEST (uadd,   unsigned char,  1U,        UCHAR_MAX, 0);
+TEST (uadd,   unsigned char,  UCHAR_MAX, UCHAR_MAX, 0);
+
+TEST (uadd,   unsigned short, 0U,        0U,         0);
+TEST (uadd,   unsigned short, 0U,        USHRT_MAX,  0);
+TEST (uadd,   unsigned short, 1U,        USHRT_MAX,  0);
+TEST (uadd,   unsigned short, USHRT_MAX, USHRT_MAX,  0);
+
+TEST (uadd,   unsigned,       0U,        0U,         0);
+TEST (uadd,   unsigned,       0U,        UINT_MAX,   0);
+TEST (uadd,   unsigned,       1U,        UINT_MAX,   1);
+TEST (uadd,   unsigned,       UINT_MAX,  UINT_MAX,   1);
+
+/* Unsigned long addition.  */
+TEST (uaddl,  unsigned long,  0UL,       0UL,       0);
+TEST (uaddl,  unsigned long,  0UL,       ULONG_MAX, 0);
+TEST (uaddl,  unsigned long,  1UL,       ULONG_MAX, 1);
+TEST (uaddl,  unsigned long,  ULONG_MAX, ULONG_MAX, 1);
+
+TEST (uaddll, unsigned long long, 0ULL,       0ULL,       0);
+TEST (uaddll, unsigned long long, 0ULL,       ULLONG_MAX, 0);
+TEST (uaddll, unsigned long long, 1ULL,       ULLONG_MAX, 1);
+TEST (uaddll, unsigned long long, ULLONG_MAX, ULLONG_MAX, 1);
+
+/* Signed int subtraction.  */
+TEST (ssub,   signed char,    0,         0,          0);
+TEST (ssub,   signed char,    0,         SCHAR_MAX,  0);
+TEST (ssub,   signed char,    1,         SCHAR_MAX,  0);
+TEST (ssub,   signed char,    SCHAR_MAX, SCHAR_MAX,  0);
+TEST (ssub,   signed char,    0,         SCHAR_MIN,  0);
+TEST (ssub,   signed char,   -1,         SCHAR_MIN,  0);
+
+TEST (ssub,   short,          0,         0,          0);
+TEST (ssub,   short,          0,         SHRT_MAX,   0);
+TEST (ssub,   short,          1,         SHRT_MAX,   0);
+TEST (ssub,   short,          SHRT_MAX,  SHRT_MAX,   0);
+TEST (ssub,   short,          0,         SHRT_MIN,   0);
+TEST (ssub,   short,         -1,         SHRT_MIN,   0);
+TEST (ssub,   short,          SHRT_MIN,  SHRT_MIN,   0);
+
+TEST (ssub,   int,            0,         0,          0);
+TEST (ssub,   int,            0,         INT_MAX,    0);
+TEST (ssub,   int,            1,         INT_MAX,    0);
+TEST (ssub,   int,            INT_MAX,   INT_MAX,    0);
+TEST (ssub,   int,            0,         INT_MIN,    1);
+TEST (ssub,   int,           -1,         INT_MIN,    0);
+TEST (ssub,   int,            INT_MIN,   INT_MIN,    0);
+
+/* Signed long subtraction.  */
+TEST (ssubl,  long,           0L,        0L,         0);
+TEST (ssubl,  long,           0L,        LONG_MAX,   0);
+TEST (ssubl,  long,           1L,        LONG_MAX,   0);
+TEST (ssubl,  long,           LONG_MAX,  LONG_MAX,   0);
+TEST (ssubl,  long,           0L,        LONG_MIN,   1);
+TEST (ssubl,  long,          -1L,        LONG_MIN,   0);
+TEST (ssubl,  long,           LONG_MIN,  LONG_MIN,   0);
+
+/* Signed long long subtraction.  */
+TEST (ssubll, long long,      0LL,       0LL,        0);
+TEST (ssubll, long long,      0LL,       LLONG_MAX,  0);
+TEST (ssubll, long long,      1LL,       LLONG_MAX,  0);
+TEST (ssubll, long long,      LLONG_MAX, LLONG_MAX,  0);
+TEST (ssubll, long long,      0LL,       LLONG_MIN,  1);
+TEST (ssubll, long long,     -1LL,       LLONG_MIN,  0);
+TEST (ssubll, long long,      LLONG_MIN, LLONG_MIN,  0);
+
+/* Unsigned int subtraction.  */
+TEST (usub,   unsigned char,  0U,        0U,         0);
+TEST (usub,   unsigned char,  0U,        UCHAR_MAX,  1);
+TEST (usub,   unsigned char,  1U,        UCHAR_MAX,  1);
+TEST (usub,   unsigned char,  UCHAR_MAX, UCHAR_MAX,  0);
+
+TEST (usub,   unsigned short, 0U,        0U,         0);
+TEST (usub,   unsigned short, 0U,        USHRT_MAX,  1);
+TEST (usub,   unsigned short, 1U,        USHRT_MAX,  1);
+TEST (usub,   unsigned short, USHRT_MAX, USHRT_MAX,  0);
+
+TEST (usub,   unsigned,       0U,        0U,         0);
+TEST (usub,   unsigned,       0U,        UINT_MAX,   1);
+TEST (usub,   unsigned,       1U,        UINT_MAX,   1);
+TEST (usub,   unsigned,       UINT_MAX,  UINT_MAX,   0);
+
+/* Unsigned long subtraction.  */
+TEST (usubl,  unsigned long,  0UL,       0UL,       0);
+TEST (usubl,  unsigned long,  0UL,       ULONG_MAX, 1);
+TEST (usubl,  unsigned long,  1UL,       ULONG_MAX, 1);
+TEST (usubl,  unsigned long,  ULONG_MAX, ULONG_MAX, 0);
+
+/* Unsigned long long subtraction.  */
+TEST (usubll, unsigned long long,  0ULL,       0ULL,       0);
+TEST (usubll, unsigned long long,  0ULL,       ULLONG_MAX, 1);
+TEST (usubll, unsigned long long,  1ULL,       ULLONG_MAX, 1);
+TEST (usubll, unsigned long long,  ULLONG_MAX, ULLONG_MAX, 0);
diff --git a/gcc/testsuite/g++.dg/ext/builtin-arith-overflow-1.C b/gcc/testsuite/g++.dg/ext/builtin-arith-overflow-1.C
new file mode 100644 (file)
index 0000000..669eea2
--- /dev/null
@@ -0,0 +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];
+template <int N> int foo (int);
+
+void
+bar ()
+{
+  foo <__builtin_add_overflow_p (B, C, C) + 1> (0);
+}