+2019-07-22 Jonathan Wakely <jwakely@redhat.com>
+
+ * include/std/bit (__ceil2): Make unrepresentable results undefined,
+ as per P1355R2. Add debug assertion. Perform one left shift, not two,
+ so that out of range values cause undefined behaviour. Ensure that
+ shift will still be undefined if left operand is promoted.
+ * testsuite/26_numerics/bit/bit.pow.two/ceil2.cc: Replace checks for
+ unrepresentable values with checks that they are not core constant
+ expressions.
+ * testsuite/26_numerics/bit/bit.pow.two/ceil2_neg.cc: New test.
+
2019-07-19 François Dumont <fdumont@gcc.gnu.org>
* include/bits/stl_tempbuf.h (__detail::__return_temporary_buffer): Fix
constexpr auto _Nd = numeric_limits<_Tp>::digits;
if (__x == 0 || __x == 1)
return 1;
- const unsigned __n = _Nd - std::__countl_zero((_Tp)(__x - 1u));
- const _Tp __y_2 = (_Tp)1u << (__n - 1u);
- return __y_2 << 1u;
+ auto __shift_exponent = _Nd - std::__countl_zero((_Tp)(__x - 1u));
+ // If the shift exponent equals _Nd then the correct result is not
+ // representable as a value of _Tp, and so the result is undefined.
+ // Want that undefined behaviour to be detected in constant expressions,
+ // by UBSan, and by debug assertions.
+#ifdef _GLIBCXX_HAVE_BUILTIN_IS_CONSTANT_EVALUATED
+ if (!__builtin_is_constant_evaluated())
+ __glibcxx_assert( __shift_exponent != numeric_limits<_Tp>::digits );
+#endif
+ using __promoted_type = decltype(__x << 1);
+ if _GLIBCXX17_CONSTEXPR (!is_same<__promoted_type, _Tp>::value)
+ {
+ // If __x undergoes integral promotion then shifting by _Nd is
+ // not undefined. In order to make the shift undefined, so that
+ // it is diagnosed in constant expressions and by UBsan, we also
+ // need to "promote" the shift exponent to be too large for the
+ // promoted type.
+ const int __extra_exp = sizeof(__promoted_type) / sizeof(_Tp) / 2;
+ __shift_exponent |= (__shift_exponent & _Nd) << __extra_exp;
+ }
+ return (_Tp)1u << __shift_exponent;
}
template<typename _Tp>
#include <bit>
+template<typename T>
+ constexpr T max = std::numeric_limits<T>::max();
+// Largest representable power of two (i.e. has most significant bit set)
+template<typename T>
+ constexpr T maxpow2 = T(1) << (std::numeric_limits<T>::digits - 1);
+
+// Detect whether std::ceil2(N) is a constant expression.
+template<auto N, typename = void>
+ struct ceil2_valid
+ : std::false_type { };
+
+template<auto N>
+ struct ceil2_valid<N, std::void_t<char[(std::ceil2(N), 1)]>>
+ : std::true_type { };
+
template<typename UInt>
constexpr auto
test(UInt x)
static_assert( std::ceil2(UInt(3) << 64) == (UInt(4) << 64) );
}
- constexpr UInt msb = UInt(1) << (std::numeric_limits<UInt>::digits - 1);
+ constexpr UInt msb = maxpow2<UInt>;
+ static_assert( ceil2_valid<msb>() );
static_assert( std::ceil2( msb ) == msb );
- // Larger values cannot be represented so the return value is unspecified,
- // but must still be valid in constant expressions, i.e. not undefined.
- static_assert( std::ceil2( UInt(msb + 1) ) != 77 );
- static_assert( std::ceil2( UInt(msb + 2) ) != 77 );
- static_assert( std::ceil2( UInt(msb + 77) ) != 77 );
+ static_assert( std::ceil2( UInt(msb - 1) ) == msb );
+ static_assert( std::ceil2( UInt(msb - 2) ) == msb );
+ static_assert( std::ceil2( UInt(msb - 3) ) == msb );
+
+ // P1355R2: not a constant expression if the result is not representable
+ static_assert( !ceil2_valid<UInt(msb + 1)>() );
+ static_assert( !ceil2_valid<max<UInt>>() );
+ static_assert( !ceil2_valid<UInt(max<UInt> - 1)>() );
+ static_assert( !ceil2_valid<UInt(max<UInt> - 2)>() );
return true;
}
--- /dev/null
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -D_GLIBCXX_ASSERTIONS" }
+// { dg-do run { target c++2a } }
+// { dg-xfail-run-if "__glibcxx_assert in ceil2 should fail" { *-*-* } }
+
+#include <bit>
+
+// P1355R2: not a constant expression if the result is not representable
+
+template<auto N, typename = void>
+ struct ceil2_valid
+ : std::false_type { };
+
+template<auto N>
+ struct ceil2_valid<N, std::void_t<char[(std::ceil2(N), 1)]>>
+ : std::true_type { };
+
+template<typename T>
+ constexpr T max = std::numeric_limits<T>::max();
+template<typename T>
+ constexpr T maxpow2 = T(1) << (std::numeric_limits<T>::digits - 1);
+
+static_assert( ceil2_valid<maxpow2<unsigned char>>() );
+static_assert( !ceil2_valid<maxpow2<unsigned char> + (unsigned char)1>() );
+
+static_assert( !ceil2_valid<max<unsigned char>>() );
+static_assert( !ceil2_valid<max<unsigned char> - (unsigned char)1>() );
+
+static_assert( ceil2_valid<maxpow2<unsigned short>>() );
+static_assert( !ceil2_valid<maxpow2<unsigned short> + (unsigned short)1>() );
+static_assert( !ceil2_valid<max<unsigned short>>() );
+static_assert( !ceil2_valid<max<unsigned short> - (unsigned short)1>() );
+
+static_assert( ceil2_valid<maxpow2<unsigned int>>() );
+static_assert( !ceil2_valid<maxpow2<unsigned int> + 1u>() );
+static_assert( !ceil2_valid<max<unsigned int>>() );
+static_assert( !ceil2_valid<max<unsigned int> - 1u>() );
+
+static_assert( ceil2_valid<maxpow2<unsigned long>>() );
+static_assert( !ceil2_valid<maxpow2<unsigned long> + 1ul>() );
+static_assert( !ceil2_valid<max<unsigned long>>() );
+static_assert( !ceil2_valid<max<unsigned long> - 1ul>() );
+
+static_assert( ceil2_valid<maxpow2<unsigned long long>>() );
+static_assert( !ceil2_valid<maxpow2<unsigned long long> + 1ull>() );
+static_assert( !ceil2_valid<max<unsigned long long>>() );
+static_assert( !ceil2_valid<max<unsigned long long> - 1ull>() );
+
+void
+test01()
+{
+ std::ceil2( maxpow2<unsigned> + 1u ); // should fail __glibcxx_assert
+}
+
+int main()
+{
+ test01();
+}