Hi! This paper from what I can see mostly just codifies our implementation-defined behavior as standard (two's complement as the only possible representation of signed integers, but still keeping UB signed integer overflows), the only changes I found that IMHO need changing in GCC are that left shifts of signed integers are now always well defined (well, like for unsigned shifts out of bounds shift count is still UB), so we need to accept such shifts in constant expressions, don't warn about those and don't sanitize it in ubsan when in -std=c++2a or -std=gnu++2a modes.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2018-11-14 Jakub Jelinek <ja...@redhat.com> P1236R1 - Signed integers are two's complement gcc/cp/ * constexpr.c (cxx_eval_check_shift_p): Disable the signed LSHIFT_EXPR checks for c++2a. gcc/c-family/ * c-warn.c (maybe_warn_shift_overflow): Don't warn for c++2a. * c-ubsan.c (ubsan_instrument_shift): Make signed shifts with in-range second operand well defined for -std=c++2a. gcc/ * doc/invoke.texi (Wshift-overflow): Adjust documentation for c++2a. gcc/testsuite/ * g++.dg/cpp2a/constexpr-shift1.C: New test. * g++.dg/warn/permissive-1.C (enum A, enum D): Don't expect diagnostics here for c++2a. * g++.dg/cpp0x/constexpr-shift1.C (fn3, i3, fn4, i4): Don't expect diagnostics here for c++2a. * g++.dg/cpp0x/constexpr-60049.C (f3, x3, y3): Likewise. * g++.dg/ubsan/cxx11-shift-1.C (main): Add some further tests. * g++.dg/ubsan/cxx11-shift-2.C (main): Likewise. * g++.dg/ubsan/cxx2a-shift-1.C: New test. * g++.dg/ubsan/cxx2a-shift-2.C: New test. --- gcc/cp/constexpr.c.jj 2018-11-08 18:07:56.071075133 +0100 +++ gcc/cp/constexpr.c 2018-11-14 08:56:23.705641740 +0100 @@ -1920,9 +1920,14 @@ cxx_eval_check_shift_p (location_t loc, 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) - && (cxx_dialect >= cxx11)) + otherwise, the behavior is undefined. + For C++2a: + The value of E1 << E2 is the unique value congruent to E1 x 2^E2 modulo + 2^N, where N is the range exponent of the type of the result. */ + if (code == LSHIFT_EXPR + && !TYPE_UNSIGNED (lhstype) + && cxx_dialect >= cxx11 + && cxx_dialect < cxx2a) { if (tree_int_cst_sgn (lhs) == -1) { --- gcc/c-family/c-warn.c.jj 2018-08-27 17:50:41.649525026 +0200 +++ gcc/c-family/c-warn.c 2018-11-14 08:59:02.424022594 +0100 @@ -2286,6 +2286,8 @@ diagnose_mismatched_attributes (tree old /* Warn if signed left shift overflows. We don't warn about left-shifting 1 into the sign bit in C++14; cf. <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3367.html#1457> + and don't warn for C++2a at all, as signed left shifts never + overflow. LOC is a location of the shift; OP0 and OP1 are the operands. Return true if an overflow is detected, false otherwise. */ @@ -2300,7 +2302,7 @@ maybe_warn_shift_overflow (location_t lo unsigned int prec0 = TYPE_PRECISION (type0); /* Left-hand operand must be signed. */ - if (TYPE_UNSIGNED (type0)) + if (TYPE_UNSIGNED (type0) || cxx_dialect >= cxx2a) return false; unsigned int min_prec = (wi::min_precision (wi::to_wide (op0), SIGNED) @@ -2309,7 +2311,7 @@ maybe_warn_shift_overflow (location_t lo * However, shifting 1 _out_ of the sign bit, as in * INT_MIN << 1, is considered an overflow. */ - if (!tree_int_cst_sign_bit(op0) && min_prec == prec0 + 1) + if (!tree_int_cst_sign_bit (op0) && min_prec == prec0 + 1) { /* Never warn for C++14 onwards. */ if (cxx_dialect >= cxx14) --- gcc/c-family/c-ubsan.c.jj 2018-11-14 00:55:32.351645152 +0100 +++ gcc/c-family/c-ubsan.c 2018-11-14 08:35:13.131600599 +0100 @@ -134,7 +134,10 @@ ubsan_instrument_shift (location_t loc, if (TYPE_OVERFLOW_WRAPS (type0) || maybe_ne (GET_MODE_BITSIZE (TYPE_MODE (type0)), TYPE_PRECISION (type0)) - || !sanitize_flags_p (SANITIZE_SHIFT_BASE)) + || !sanitize_flags_p (SANITIZE_SHIFT_BASE) + /* In C++2a and later, shifts are well defined except when + the second operand is not within bounds. */ + || cxx_dialect >= cxx2a) ; /* For signed x << y, in C99/C11, the following: --- gcc/doc/invoke.texi.jj 2018-11-14 00:55:36.636576201 +0100 +++ gcc/doc/invoke.texi 2018-11-14 13:05:54.055418228 +0100 @@ -5074,11 +5074,12 @@ This is the warning level of @option{-Ws by default in C99 and C++11 modes (and newer). This warning level does not warn about left-shifting 1 into the sign bit. (However, in C, such an overflow is still rejected in contexts where an integer constant expression -is required.) +is required.) No warning is emitted in C++2A mode (and newer), as signed left +shifts always wrap. @item -Wshift-overflow=2 This warning level also warns about left-shifting 1 into the sign bit, -unless C++14 mode is active. +unless C++14 mode (or newer) is active. @end table @item -Wswitch --- gcc/testsuite/g++.dg/cpp2a/constexpr-shift1.C.jj 2018-11-14 09:11:57.355234640 +0100 +++ gcc/testsuite/g++.dg/cpp2a/constexpr-shift1.C 2018-11-14 09:10:58.382207816 +0100 @@ -0,0 +1,25 @@ +// { dg-do compile { target c++11 } } + +constexpr int a = -42 << 0; // { dg-error "left operand of shift expression '\\(-42 << 0\\)' is negative" "" { target c++17_down } } +constexpr int b = -42 << 1; // { dg-error "left operand of shift expression '\\(-42 << 1\\)' is negative" "" { target c++17_down } } +constexpr int c = -42 << (__SIZEOF_INT__ * __CHAR_BIT__ - 1); // { dg-error "left operand of shift expression '\\(-42 << \[0-9]*\\)' is negative" "" { target c++17_down } } + // { dg-warning "result of '\\(-42 << \[0-9]*\\)' requires \[0-9]* bits to represent, but 'int' only has \[0-9]* bits" "" { target c++17_down } .-1 } +constexpr int d = 42 << (__SIZEOF_INT__ * __CHAR_BIT__ - 1); // { dg-error "shift expression '\\(42 << \[0-9]*\\)' overflows" "" { target c++17_down } } + // { dg-warning "result of '\\(42 << \[0-9]*\\)' requires \[0-9]* bits to represent, but 'int' only has \[0-9]* bits" "" { target c++17_down } .-1 } +constexpr int e = 32 << (__SIZEOF_INT__ * __CHAR_BIT__ - 5); // { dg-error "shift expression '\\(32 << \[0-9]*\\)' overflows" "" { target c++17_down } } + // { dg-warning "result of '\\(32 << \[0-9]*\\)' requires \[0-9]* bits to represent, but 'int' only has \[0-9]* bits" "" { target c++17_down } .-1 } +constexpr int f = 32 << (__SIZEOF_INT__ * __CHAR_BIT__ - 6); +constexpr int g = -42U << 0; +constexpr int h = -42U << 1; +constexpr int i = -42U << (__SIZEOF_INT__ * __CHAR_BIT__ - 1); +constexpr int j = 42U << (__SIZEOF_INT__ * __CHAR_BIT__ - 1); +constexpr int k = 32U << (__SIZEOF_INT__ * __CHAR_BIT__ - 5); +constexpr int l = 32U << (__SIZEOF_INT__ * __CHAR_BIT__ - 6); +#if __cplusplus > 201703L +static_assert (a == g); +static_assert (b == h); +static_assert (c == i); +static_assert (d == j); +static_assert (e == k); +static_assert (f == l); +#endif --- gcc/testsuite/g++.dg/warn/permissive-1.C.jj 2016-01-12 19:22:56.760037087 +0100 +++ gcc/testsuite/g++.dg/warn/permissive-1.C 2018-11-14 09:36:47.658634223 +0100 @@ -2,7 +2,7 @@ // { dg-do compile { target int32 } } // { dg-options "-fpermissive -Wno-shift-overflow -Wno-shift-count-overflow -Wno-shift-count-negative" } -enum A { AA = -1 << 4 }; // { dg-warning "operand of shift expression" "" { target c++11 } } +enum A { AA = -1 << 4 }; // { dg-warning "operand of shift expression" "" { target { c++11 && c++17_down } } } enum B { BB = 1 << -4 }; // { dg-warning "operand of shift expression" } enum C { CC = 1 << __SIZEOF_INT__ * 4 * __CHAR_BIT__ - 4 }; // { dg-warning "operand of shift expression" } -enum D { DD = 10 << __SIZEOF_INT__ * __CHAR_BIT__ - 2 }; // { dg-warning "shift expression" "" { target c++11 } } +enum D { DD = 10 << __SIZEOF_INT__ * __CHAR_BIT__ - 2 }; // { dg-warning "shift expression" "" { target { c++11 && c++17_down } } } --- gcc/testsuite/g++.dg/cpp0x/constexpr-shift1.C.jj 2017-11-21 20:22:57.851043093 +0100 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-shift1.C 2018-11-14 09:14:31.240695295 +0100 @@ -19,18 +19,18 @@ constexpr int i2 = fn2 (1, 200); // { dg constexpr int fn3 (int i, int j) { - return i << j; // { dg-error "is negative" } + return i << j; // { dg-error "is negative" "" { target c++17_down } } } -constexpr int i3 = fn3 (-1, 2); // { dg-message "in .constexpr. expansion of " } +constexpr int i3 = fn3 (-1, 2); // { dg-message "in .constexpr. expansion of " "" { target c++17_down } } constexpr int fn4 (int i, int j) { - return i << j; // { dg-error "overflows" } + return i << j; // { dg-error "overflows" "" { target c++17_down } } } -constexpr int i4 = fn4 (__INT_MAX__, 2); // { dg-message "in .constexpr. expansion of " } +constexpr int i4 = fn4 (__INT_MAX__, 2); // { dg-message "in .constexpr. expansion of " "" { target c++17_down } } constexpr int fn5 (int i, int j) --- gcc/testsuite/g++.dg/cpp0x/constexpr-60049.C.jj 2017-11-21 20:22:57.870042857 +0100 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-60049.C 2018-11-14 09:35:29.599922164 +0100 @@ -5,7 +5,7 @@ constexpr int f1 (int n) { return 1 << n; } // { dg-error "shift expression" } constexpr int f2 (int n) { return 1 << n; } // { dg-error "shift expression" } -constexpr int f3 (int n) { return n << 1; } // { dg-error "shift expression" } +constexpr int f3 (int n) { return n << 1; } // { dg-error "shift expression" "" { target c++17_down } } constexpr int f4 (int n) { return 1 >> n; } // { dg-error "shift expression" } constexpr int f5 (int n) { return 1 >> n; } // { dg-error "shift expression" } @@ -13,12 +13,12 @@ constexpr int X = __CHAR_BIT__ * sizeof constexpr int x1 = f1 (X); // { dg-message "in .constexpr. expansion of" } constexpr int x2 = f2 (-1); // { dg-message "in .constexpr. expansion of" } -constexpr int x3 = f3 (-1); // { dg-message "in .constexpr. expansion of" } +constexpr int x3 = f3 (-1); // { dg-message "in .constexpr. expansion of" "" { target c++17_down } } constexpr int x4 = f4 (X); // { dg-message "in .constexpr. expansion of" } constexpr int x5 = f5 (-1); // { dg-message "in .constexpr. expansion of" } constexpr int y1 = 1 << X; // { dg-error "shift expression" } constexpr int y2 = 1 << -1; // { dg-error "shift expression" } -constexpr int y3 = -1 << 1; // { dg-error "shift expression" } +constexpr int y3 = -1 << 1; // { dg-error "shift expression" "" { target c++17_down } } constexpr int y4 = 1 >> X; // { dg-error "shift expression" } constexpr int y5 = 1 >> -1; // { dg-error "shift expression" } --- gcc/testsuite/g++.dg/ubsan/cxx11-shift-1.C.jj 2014-10-22 15:52:16.527879247 +0200 +++ gcc/testsuite/g++.dg/ubsan/cxx11-shift-1.C 2018-11-14 08:43:15.806640709 +0100 @@ -2,9 +2,10 @@ /* { dg-options "-fsanitize=shift -w -fno-sanitize-recover=shift -std=c++11" } */ int -main (void) +main () { int a = 1; - a <<= 31; - return 0; + a <<= (__SIZEOF_INT__ * __CHAR_BIT__ - 1); + a = 16; + a <<= (__SIZEOF_INT__ * __CHAR_BIT__ - 5); } --- gcc/testsuite/g++.dg/ubsan/cxx11-shift-2.C.jj 2014-04-23 10:18:41.089330171 +0200 +++ gcc/testsuite/g++.dg/ubsan/cxx11-shift-2.C 2018-11-14 08:43:07.947770370 +0100 @@ -2,9 +2,18 @@ /* { dg-options "-fsanitize=shift -w -std=c++11" } */ int -main (void) +main () { int a = -42; a <<= 1; + a = -43; + a <<= 0; + a = -44; + a <<= (__SIZEOF_INT__ * __CHAR_BIT__ - 1); + a = 32; + a <<= (__SIZEOF_INT__ * __CHAR_BIT__ - 3); } -/* { dg-output "left shift of negative value -42" } */ +/* { dg-output "left shift of negative value -42.*" } */ +/* { dg-output "left shift of negative value -43.*" } */ +/* { dg-output "left shift of negative value -44.*" } */ +/* { dg-output "left shift of 32 by \[0-9]* places cannot be represented in type 'int'" } */ --- gcc/testsuite/g++.dg/ubsan/cxx2a-shift-1.C.jj 2018-11-14 08:35:13.132600583 +0100 +++ gcc/testsuite/g++.dg/ubsan/cxx2a-shift-1.C 2018-11-14 08:42:56.229963690 +0100 @@ -0,0 +1,11 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift -w -fno-sanitize-recover=shift -std=c++2a" } */ + +int +main () +{ + int a = 1; + a <<= 31; + a = 16; + a <<= (__SIZEOF_INT__ * __CHAR_BIT__ - 5); +} --- gcc/testsuite/g++.dg/ubsan/cxx2a-shift-2.C.jj 2018-11-14 08:35:13.132600583 +0100 +++ gcc/testsuite/g++.dg/ubsan/cxx2a-shift-2.C 2018-11-14 08:42:48.803086219 +0100 @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=shift -w -fno-sanitize-recover=shift -std=c++2a" } */ + +int +main () +{ + int a = -42; + a <<= 1; + a = -43; + a <<= 0; + a = -44; + a <<= (__SIZEOF_INT__ * __CHAR_BIT__ - 1); + a = 32; + a <<= (__SIZEOF_INT__ * __CHAR_BIT__ - 3); +} Jakub