From c06617a79b41da37d80d7e88a3dbc56818f3e706 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Thu, 8 Oct 2020 14:01:00 +0100 Subject: [PATCH] libstdc++: Avoid divide by zero in default template arguments My previous attempt to fix this only worked when m is a power of two. There is still a bug when a=00 and !has_single_bit(m). Instead of trying to make _Mod work for a==0 this change ensures that we never instantiate it with a==0. For C++17 we can use if-constexpr, but otherwise we need to use a different multipler. It doesn't matter what we use, as it won't actually be called, only instantiated. libstdc++-v3/ChangeLog: * include/bits/random.h (__detail::_Mod): Revert last change. (__detail::__mod): Do not use _Mod for a==0 case. * testsuite/26_numerics/random/linear_congruential_engine/operators/call.cc: Check other cases with a==0. Also check runtime results. * testsuite/26_numerics/random/pr60037-neg.cc: Adjust dg-error line. --- libstdc++-v3/include/bits/random.h | 13 +++++- .../operators/call.cc | 43 +++++++++++++++++-- .../26_numerics/random/pr60037-neg.cc | 2 +- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/libstdc++-v3/include/bits/random.h b/libstdc++-v3/include/bits/random.h index 920f3d91513..0be1191e07d 100644 --- a/libstdc++-v3/include/bits/random.h +++ b/libstdc++-v3/include/bits/random.h @@ -109,7 +109,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template= __m - 1), - bool __schrage_ok = __a != 0 && __m % __a < __m / __a> + bool __schrage_ok = __m % __a < __m / __a> struct _Mod { typedef typename _Select_uint_least_t inline _Tp __mod(_Tp __x) - { return _Mod<_Tp, __m, __a, __c>::__calc(__x); } + { + if _GLIBCXX17_CONSTEXPR (__a == 0) + return __c; + else + { + // _Mod must not be instantiated with a == 0 + constexpr _Tp __a1 = __a ? __a : 1; + return _Mod<_Tp, __m, __a1, __c>::__calc(__x); + } + } /* * An adaptor class for converting the output of any Generator into diff --git a/libstdc++-v3/testsuite/26_numerics/random/linear_congruential_engine/operators/call.cc b/libstdc++-v3/testsuite/26_numerics/random/linear_congruential_engine/operators/call.cc index d1fff6d0a5d..0000aa2402f 100644 --- a/libstdc++-v3/testsuite/26_numerics/random/linear_congruential_engine/operators/call.cc +++ b/libstdc++-v3/testsuite/26_numerics/random/linear_congruential_engine/operators/call.cc @@ -15,13 +15,50 @@ // with this library; see the file COPYING3. If not see // . -// { dg-do compile { target c++11 } } +// { dg-do run { target c++11 } } #include +#include -unsigned +void test01() { std::linear_congruential_engine l; - return l(); // this used to result in divide by zero + auto r = l(); // this used to result in divide by zero + VERIFY( r == 0 ); + l.seed(2); + r = l(); + VERIFY( r == 0 ); + VERIFY( l() == 0 ); +} + +void +test02() +{ + std::linear_congruential_engine l; + auto r = l(); // this used to result in a different divide by zero + VERIFY( r == 0 ); + l.seed(2); + r = l(); + VERIFY( r == 0 ); + VERIFY( l() == 0 ); +} + +void +test03() +{ + std::linear_congruential_engine l; + auto r = l(); + VERIFY( r == 2 ); + l.seed(4); + r = l(); + VERIFY( r == 2 ); + VERIFY( l() == 2 ); +} + +int main() +{ + test01(); + test02(); + test03(); } diff --git a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc index f808132e9ea..139abbb3051 100644 --- a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc +++ b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc @@ -10,6 +10,6 @@ std::__detail::_Adaptor aurng(urng); auto x = std::generate_canonical::digits>(urng); -// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 158 } +// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 167 } // { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3281 } -- 2.30.2