[C++ PATCH] Detect UB in shifts in constexpr functions
Marek Polacek
polacek@redhat.com
Mon Nov 24 14:03:00 GMT 2014
Constant expressions can't contain undefined behavior, which means that
using an expression containing UB in a context requiring a constant
expression makes the program invalid and we're required to diagnose that.
We fail to diagnose UB if using shifts, because fold_binary_loc returns
zero for a shift containing UB. So I wrote a function that checks whether
the shift operation is ok.
I'm not sure yet if anything else needs similar treatment.
Bootstrapped/regtested on ppc64-linux, ok for trunk?
2014-11-24 Marek Polacek <polacek@redhat.com>
* constexpr.c (cxx_eval_check_shift_p): New function.
(cxx_eval_binary_expression): Call it.
* g++.dg/cpp0x/constexpr-shift1.C: New test.
diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 2678223..2047a09 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -1451,6 +1451,43 @@ verify_constant (tree t, bool allow_non_constant, bool *non_constant_p,
return *non_constant_p;
}
+/* Return true if the shift operation on LHS and RHS is undefined. */
+
+static bool
+cxx_eval_check_shift_p (enum tree_code code, tree lhs, tree rhs)
+{
+ if (code != LSHIFT_EXPR && code != RSHIFT_EXPR)
+ return false;
+
+ tree lhstype = TREE_TYPE (lhs);
+ unsigned HOST_WIDE_INT uprec = TYPE_PRECISION (TREE_TYPE (lhs));
+
+ /* [expr.shift] The behavior is undefined if the right operand
+ is negative, or greater than or equal to the length in bits
+ of the promoted left operand. */
+ if (tree_int_cst_sgn (rhs) == -1 || compare_tree_int (rhs, uprec) >= 0)
+ return true;
+
+ /* The value of E1 << E2 is E1 left-shifted E2 bit positions; [...]
+ if E1 has a signed type and non-negative value, and E1x2^E2 is
+ representable in the corresponding unsigned type of the result type,
+ then that value, converted to the result type, is the resulting value;
+ otherwise, the behavior is undefined. */
+ if (code == LSHIFT_EXPR && !TYPE_UNSIGNED (lhstype))
+ {
+ if (tree_int_cst_sgn (lhs) == -1)
+ return true;
+ tree t = build_int_cst (unsigned_type_node, uprec - 1);
+ t = fold_build2 (MINUS_EXPR, unsigned_type_node, t, rhs);
+ tree ulhs = fold_convert (unsigned_type_for (lhstype), lhs);
+ t = fold_build2 (RSHIFT_EXPR, TREE_TYPE (ulhs), ulhs, t);
+ if (tree_int_cst_lt (integer_one_node, t))
+ return true;
+ }
+
+ return false;
+}
+
/* Subroutine of cxx_eval_constant_expression.
Attempt to reduce the unary expression tree T to a compile time value.
If successful, return the value. Otherwise issue a diagnostic
@@ -1506,7 +1543,7 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
enum tree_code code = TREE_CODE (t);
tree type = TREE_TYPE (t);
r = fold_binary_loc (loc, code, type, lhs, rhs);
- if (r == NULL_TREE)
+ if (r == NULL_TREE || cxx_eval_check_shift_p (code, lhs, rhs))
{
if (lhs == orig_lhs && rhs == orig_rhs)
r = t;
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-shift1.C gcc/testsuite/g++.dg/cpp0x/constexpr-shift1.C
index e69de29..2551fbe 100644
--- gcc/testsuite/g++.dg/cpp0x/constexpr-shift1.C
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-shift1.C
@@ -0,0 +1,73 @@
+// { dg-do compile { target c++11 } }
+
+constexpr int
+fn1 (int i, int j)
+{
+ return i << j; // { dg-error "is not a constant expression" }
+}
+
+constexpr int i1 = fn1 (1, -1);
+
+constexpr int
+fn2 (int i, int j)
+{
+ return i << j; // { dg-error "is not a constant expression" }
+}
+
+constexpr int i2 = fn2 (1, 200);
+
+constexpr int
+fn3 (int i, int j)
+{
+ return i << j; // { dg-error "is not a constant expression" }
+}
+
+constexpr int i3 = fn3 (-1, 2);
+
+constexpr int
+fn4 (int i, int j)
+{
+ return i << j; // { dg-error "is not a constant expression" }
+}
+
+constexpr int i4 = fn4 (__INT_MAX__, 2);
+
+constexpr int
+fn5 (int i, int j)
+{
+ return i << j;
+}
+
+constexpr int i5 = fn5 (__INT_MAX__, 1);
+
+constexpr int
+fn6 (unsigned int i, unsigned int j)
+{
+ return i << j; // { dg-error "is not a constant expression" }
+}
+
+constexpr int i6 = fn6 (1, -1);
+
+constexpr int
+fn7 (int i, int j)
+{
+ return i >> j; // { dg-error "is not a constant expression" }
+}
+
+constexpr int i7 = fn7 (1, -1);
+
+constexpr int
+fn8 (int i, int j)
+{
+ return i >> j;
+}
+
+constexpr int i8 = fn8 (-1, 1);
+
+constexpr int
+fn9 (int i, int j)
+{
+ return i >> j; // { dg-error "is not a constant expression" }
+}
+
+constexpr int i9 = fn9 (1, 200);
Marek
More information about the Gcc-patches
mailing list