From 8a9dee44a6c04bbd1255ae4174ca8b133c282c94 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Wed, 21 Jan 2026 14:24:45 +0800 Subject: [PATCH 1/7] Remove non-Standard inheritance from piecewise distributions --- stl/inc/random | 101 +++++++++--------- tests/std/test.lst | 1 + .../GH_001600_random_inheritance/env.lst | 4 + .../test.compile.pass.cpp | 68 ++++++++++++ 4 files changed, 122 insertions(+), 52 deletions(-) create mode 100644 tests/std/tests/GH_001600_random_inheritance/env.lst create mode 100644 tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp diff --git a/stl/inc/random b/stl/inc/random index f6d03829529..7e43dcb0b94 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -4221,26 +4221,22 @@ public: }; _EXPORT_STD template -class piecewise_constant_distribution - : public discrete_distribution { // piecewise constant floating-point distribution - // TRANSITION: unused _Mypbase subobject from base class +class piecewise_constant_distribution { // piecewise constant floating-point distribution public: _RNG_REQUIRE_REALTYPE(piecewise_constant_distribution, _Ty); - using _Mybase = discrete_distribution; - using _Mypbase = typename _Mybase::param_type; using result_type = _Ty; - struct param_type : _Mypbase { // parameter package + struct param_type { // parameter package using distribution_type = piecewise_constant_distribution; param_type() : _Bvec{0, 1} {} template - param_type(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2) : _Mypbase(_Noinit), _Bvec(_First1, _Last1) { + param_type(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2) : _Base_params(_Noinit), _Bvec(_First1, _Last1) { if (2 <= _Bvec.size()) { for (size_t _Idx = 0; _Idx < _Bvec.size() - 1; ++_Idx) { - this->_Pvec.push_back(static_cast(*_First2++)); + _Base_params._Pvec.push_back(static_cast(*_First2++)); } } else { // default construct _Bvec = {0, 1}; @@ -4250,12 +4246,12 @@ public: } template - param_type(initializer_list<_Ty> _Ilist, _Fn _Func) : _Mypbase(_Noinit) { + param_type(initializer_list<_Ty> _Ilist, _Fn _Func) : _Base_params(_Noinit) { if (2 <= _Ilist.size()) { _Bvec.assign(_Ilist); for (size_t _Idx = 0; _Idx < _Bvec.size() - 1; ++_Idx) { - this->_Pvec.push_back(_Func(_Ty{0.5} * (_Bvec[_Idx] + _Bvec[_Idx + 1]))); + _Base_params._Pvec.push_back(_Func(_Ty{0.5} * (_Bvec[_Idx] + _Bvec[_Idx + 1]))); } } else { // default construct _Bvec = {0, 1}; @@ -4265,7 +4261,7 @@ public: } template - param_type(size_t _Count, _Ty _Low, _Ty _High, _Fn _Func) : _Mypbase(_Count, _Low, _High, _Func) { + param_type(size_t _Count, _Ty _Low, _Ty _High, _Fn _Func) : _Base_params(_Count, _Low, _High, _Func) { _Ty _Range = _High - _Low; if (_Count <= 0) { _Count = 1; @@ -4278,8 +4274,7 @@ public: } _NODISCARD friend bool operator==(const param_type& _Left, const param_type& _Right) { - return static_cast(_Left) == static_cast(_Right) - && _Left._Bvec == _Right._Bvec; + return _Left._Base_params == _Right._Base_params && _Left._Bvec == _Right._Bvec; } #if !_HAS_CXX20 @@ -4295,7 +4290,7 @@ public: #pragma warning(push) #pragma warning(disable : 4244) // '%s': conversion from '%s' to '%s', possible loss of data _NODISCARD vector<_Ty> densities() const { - vector<_Ty> _Ans(this->_Pvec.begin(), this->_Pvec.end()); + vector<_Ty> _Ans(_Base_params._Pvec.begin(), _Base_params._Pvec.end()); for (size_t _Idx = 0; _Idx < _Ans.size(); ++_Idx) { _Ans[_Idx] /= _Bvec[_Idx + 1] - _Bvec[_Idx]; @@ -4306,9 +4301,10 @@ public: #pragma warning(pop) void _Init() { // initialize - _Mypbase::_Init(); + _Base_params._Init(); } + discrete_distribution::param_type _Base_params; vector<_Ty> _Bvec; }; @@ -4376,10 +4372,11 @@ public: template friend basic_istream<_Elem, _Traits>& operator>>(basic_istream<_Elem, _Traits>& _Istr, piecewise_constant_distribution& _Dist) { // read state from _Istr - static_cast(_Dist._Par)._Read(_Istr); + _Dist._Par._Base_params._Read(_Istr); _Dist._Par._Bvec.clear(); - for (size_t _Idx = _Dist._Par._Pvec.size() + 1; 0 < _Idx; --_Idx) { // get a value and add to intervals vector + for (size_t _Idx = _Dist._Par._Base_params._Pvec.size() + 1; 0 < _Idx; --_Idx) { + // get a value and add to intervals vector double _Val; _In(_Istr, _Val); _Dist._Par._Bvec.push_back(_Val); @@ -4390,7 +4387,7 @@ public: template friend basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, const piecewise_constant_distribution& _Dist) { // write state to _Ostr - static_cast(_Dist._Par)._Write(_Ostr); + _Dist._Par._Base_params._Write(_Ostr); for (const auto& _Val : _Dist._Par._Bvec) { _Out(_Ostr, _Val); @@ -4401,39 +4398,34 @@ public: template result_type _Eval(_Engine& _Eng, const param_type& _Par0) _DISTRIBUTION_CONST { - size_t _Px = _Mybase::operator()(_Eng, _Par0); + size_t _Px = _Base_distribution(_Eng, _Par0._Base_params); uniform_real_distribution<_Ty> _Dist(_Par0._Bvec[_Px], _Par0._Bvec[_Px + 1]); return _Dist(_Eng); } + discrete_distribution _Base_distribution; // TRANSITION, ABI: unused _Par subobject param_type _Par; }; _EXPORT_STD template -class piecewise_linear_distribution - : public discrete_distribution { // piecewise linear floating-point distribution - // TRANSITION: unused _Mypbase subobject from base class +class piecewise_linear_distribution { // piecewise linear floating-point distribution public: _RNG_REQUIRE_REALTYPE(piecewise_linear_distribution, _Ty); - using _Mybase = discrete_distribution; - using _Mypbase = typename _Mybase::param_type; using result_type = _Ty; - struct param_type : _Mypbase { // parameter package - // TRANSITION, ABI: stores probability densities (N + 1 elements) in _Mybase::_Pvec - // this breaks invariants of discrete_distribution::param_type + struct param_type { // parameter package using distribution_type = piecewise_linear_distribution; param_type() : _Bvec{0, 1} { - this->_Pvec.push_back(1.0); + _Base_params._Pvec.push_back(1.0); } template - param_type(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2) : _Mypbase(_Noinit), _Bvec(_First1, _Last1) { + param_type(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2) : _Base_params(_Noinit), _Bvec(_First1, _Last1) { if (2 <= _Bvec.size()) { for (size_t _Idx = 0; _Idx < _Bvec.size(); ++_Idx) { - this->_Pvec.push_back(static_cast(*_First2++)); + _Base_params._Pvec.push_back(static_cast(*_First2++)); } } else { // default construct _Bvec = {0, 1}; @@ -4443,12 +4435,12 @@ public: } template - param_type(initializer_list<_Ty> _Ilist, _Fn _Func) : _Mypbase(_Noinit) { + param_type(initializer_list<_Ty> _Ilist, _Fn _Func) : _Base_params(_Noinit) { if (2 <= _Ilist.size()) { _Bvec.assign(_Ilist); for (const auto& _Bval : _Bvec) { - this->_Pvec.push_back(_Func(_Bval)); + _Base_params._Pvec.push_back(_Func(_Bval)); } } else { // default construct _Bvec = {0, 1}; @@ -4458,7 +4450,7 @@ public: } template - param_type(size_t _Count, _Ty _Low, _Ty _High, _Fn _Func) : _Mypbase(_Noinit) { + param_type(size_t _Count, _Ty _Low, _Ty _High, _Fn _Func) : _Base_params(_Noinit) { _Ty _Range = _High - _Low; _STL_ASSERT(_Ty{0} < _Range, "invalid range for piecewise_linear_distribution"); if (_Count < 1) { @@ -4469,14 +4461,13 @@ public: for (size_t _Idx = 0; _Idx <= _Count; ++_Idx) { // compute _Bvec and _Pvec _Ty _Bval = _Low + _Idx * _Range; _Bvec.push_back(_Bval); - this->_Pvec.push_back(_Func(_Bval)); + _Base_params._Pvec.push_back(_Func(_Bval)); } _Init(); } _NODISCARD friend bool operator==(const param_type& _Left, const param_type& _Right) { - return static_cast(_Left) == static_cast(_Right) - && _Left._Bvec == _Right._Bvec; + return _Left._Base_params == _Right._Base_params && _Left._Bvec == _Right._Bvec; } #if !_HAS_CXX20 @@ -4492,47 +4483,51 @@ public: #pragma warning(push) #pragma warning(disable : 4244) // '%s': conversion from '%s' to '%s', possible loss of data _NODISCARD vector<_Ty> densities() const { - vector<_Ty> _Ans(this->_Pvec.begin(), this->_Pvec.end()); + vector<_Ty> _Ans(_Base_params._Pvec.begin(), _Base_params._Pvec.end()); return _Ans; } #pragma warning(pop) _NODISCARD double _Piece_probability(const size_t _Idx) const { - return 0.5 * (this->_Pvec[_Idx] + this->_Pvec[_Idx + 1]) + return 0.5 * (_Base_params._Pvec[_Idx] + _Base_params._Pvec[_Idx + 1]) * static_cast(_Bvec[_Idx + 1] - _Bvec[_Idx]); } void _Init(bool _Renorm = true) { // initialize - size_t _Size = this->_Pvec.size(); + size_t _Size = _Base_params._Pvec.size(); size_t _Idx; if (_Renorm) { - if (this->_Pvec.empty()) { // make empty vector degenerate - this->_Pvec = {1.0, 1.0}; + if (_Base_params._Pvec.empty()) { // make empty vector degenerate + _Base_params._Pvec = {1.0, 1.0}; } else { // normalize probabilities double _Sum = 0; - _STL_ASSERT(0.0 <= this->_Pvec[0], "invalid probability for piecewise_linear_distribution"); + _STL_ASSERT(0.0 <= _Base_params._Pvec[0], "invalid probability for piecewise_linear_distribution"); for (_Idx = 1; _Idx < _Size; ++_Idx) { // sum all probabilities - _STL_ASSERT(0.0 <= this->_Pvec[_Idx], "invalid probability for piecewise_linear_distribution"); + _STL_ASSERT( + 0.0 <= _Base_params._Pvec[_Idx], "invalid probability for piecewise_linear_distribution"); _Sum += _Piece_probability(_Idx - 1); } _STL_ASSERT(0.0 < _Sum, "invalid probability vector for piecewise_linear_distribution"); if (_Sum != 1.0) { for (_Idx = 0; _Idx < _Size; ++_Idx) { - this->_Pvec[_Idx] /= _Sum; + _Base_params._Pvec[_Idx] /= _Sum; } } } } - this->_Pcdf.assign(1, _Piece_probability(0)); + _Base_params._Pcdf.assign(1, _Piece_probability(0)); for (_Idx = 2; _Idx < _Size; ++_Idx) { - this->_Pcdf.push_back(_Piece_probability(_Idx - 1) + this->_Pcdf[_Idx - 2]); + _Base_params._Pcdf.push_back(_Piece_probability(_Idx - 1) + _Base_params._Pcdf[_Idx - 2]); } } + // TRANSITION, ABI: stores probability densities (N + 1 elements) in _Base_params._Pvec + // this breaks invariants of discrete_distribution::param_type + discrete_distribution::param_type _Base_params; vector<_Ty> _Bvec; }; @@ -4600,10 +4595,11 @@ public: template friend basic_istream<_Elem, _Traits>& operator>>(basic_istream<_Elem, _Traits>& _Istr, piecewise_linear_distribution& _Dist) { // read state from _Istr - static_cast(_Dist._Par)._Read(_Istr); + _Dist._Par._Base_params._Read(_Istr); _Dist._Par._Bvec.clear(); - for (size_t _Idx = _Dist._Par._Pvec.size(); 0 < _Idx; --_Idx) { // get a value and add to intervals vector + for (size_t _Idx = _Dist._Par._Base_params._Pvec.size(); 0 < _Idx; --_Idx) { + // get a value and add to intervals vector double _Val; _In(_Istr, _Val); _Dist._Par._Bvec.push_back(_Val); @@ -4615,7 +4611,7 @@ public: template friend basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, const piecewise_linear_distribution& _Dist) { // write state to _Ostr - static_cast(_Dist._Par)._Write(_Ostr); + _Dist._Par._Base_params._Write(_Ostr); for (const auto& _Val : _Dist._Par._Bvec) { _Out(_Ostr, _Val); @@ -4626,9 +4622,9 @@ public: template result_type _Eval(_Engine& _Eng, const param_type& _Par0) _DISTRIBUTION_CONST { - size_t _Px = _Mybase::operator()(_Eng, _Par0); - double _Px0 = _Par0._Pvec[_Px]; - double _Px1 = _Par0._Pvec[_Px + 1]; + size_t _Px = _Base_distribution(_Eng, _Par0._Base_params); + double _Px0 = _Par0._Base_params._Pvec[_Px]; + double _Px1 = _Par0._Base_params._Pvec[_Px + 1]; uniform_real_distribution<_Ty> _Dist; result_type _Xx0 = _Dist(_Eng); @@ -4640,6 +4636,7 @@ public: return _Par0._Bvec[_Px] + _Xx0 * (_Par0._Bvec[_Px + 1] - _Par0._Bvec[_Px]); } + discrete_distribution _Base_distribution; // TRANSITION, ABI: unused _Par subobject param_type _Par; }; diff --git a/tests/std/test.lst b/tests/std/test.lst index 5bb68adcb1d..85d7fde5fe5 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -202,6 +202,7 @@ tests\GH_001411_core_headers tests\GH_001530_binomial_accuracy tests\GH_001541_case_sensitive_boolalpha tests\GH_001596_adl_proof_algorithms +tests\GH_001600_random_inheritance tests\GH_001638_dllexport_derived_classes tests\GH_001850_clog_tied_to_cout tests\GH_001858_iostream_exception diff --git a/tests/std/tests/GH_001600_random_inheritance/env.lst b/tests/std/tests/GH_001600_random_inheritance/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_001600_random_inheritance/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp b/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp new file mode 100644 index 00000000000..4e806f1d42d --- /dev/null +++ b/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +using namespace std; + +// GH-1600 ": piecewise_linear_distribution::param_type should not inherit from +// discrete_distribution::param_type" +// Note that inheritance between distribution type should also be removed. +template +void test_gh_1600_inheritance() { // COMPILE-ONLY + STATIC_ASSERT(!is_base_of_v, piecewise_constant_distribution>); + STATIC_ASSERT(!is_base_of_v::param_type, + typename piecewise_constant_distribution::param_type>); + + STATIC_ASSERT(!is_base_of_v, piecewise_linear_distribution>); + STATIC_ASSERT(!is_base_of_v::param_type, + typename piecewise_linear_distribution::param_type>); +} + +// Until 2026-01, piecewise_{constant,linear}_distribution were both derived from discrete_distribution. +// Same for their param_type. +// Now the discrete_distribution objects are changed from base class subobjects to leading member subobjects. +// Same for corresponding param_type subobjects. +// In vNext, we should probably remove unused members, so the sizes will be reduced. +template +void test_gh_1600_abi() { // COMPILE-ONLY + // piecewise_{constant,linear}_distribution and their param_type don't introduce padding bytes under MSVC ABI. + // So it's OK to just add the sizes. + + // The sizes will probably reduced in vNext. + + STATIC_ASSERT( + sizeof(piecewise_constant_distribution) + == sizeof(discrete_distribution) + sizeof(typename piecewise_constant_distribution::param_type)); + STATIC_ASSERT(sizeof(typename piecewise_constant_distribution::param_type) + == sizeof(discrete_distribution::param_type) + sizeof(vector)); + + STATIC_ASSERT( + sizeof(piecewise_linear_distribution) + == sizeof(discrete_distribution) + sizeof(typename piecewise_linear_distribution::param_type)); + STATIC_ASSERT(sizeof(typename piecewise_linear_distribution::param_type) + == sizeof(discrete_distribution::param_type) + sizeof(vector)); + + // The alignments are likely to be unchanged in vNext. + + STATIC_ASSERT(alignof(piecewise_constant_distribution) == alignof(void*)); + STATIC_ASSERT(alignof(typename piecewise_constant_distribution::param_type) == alignof(void*)); + + STATIC_ASSERT(alignof(piecewise_linear_distribution) == alignof(void*)); + STATIC_ASSERT(alignof(typename piecewise_linear_distribution::param_type) == alignof(void*)); +} + +void test() { // COMPILE-ONLY + test_gh_1600_inheritance(); + test_gh_1600_inheritance(); + test_gh_1600_inheritance(); + + test_gh_1600_abi(); + test_gh_1600_abi(); + test_gh_1600_abi(); +} From cfe1ed9320d640ec4e7b498d8dccd9164b5d17ea Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 22 Jan 2026 09:12:06 +0800 Subject: [PATCH 2/7] Fix comments --- stl/inc/random | 4 ++-- .../GH_001600_random_inheritance/test.compile.pass.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/stl/inc/random b/stl/inc/random index 7e43dcb0b94..893c7f76d87 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -4403,7 +4403,7 @@ public: return _Dist(_Eng); } - discrete_distribution _Base_distribution; // TRANSITION, ABI: unused _Par subobject + discrete_distribution _Base_distribution; // TRANSITION, ABI: unused _Base_distribution._Par subobject param_type _Par; }; @@ -4636,7 +4636,7 @@ public: return _Par0._Bvec[_Px] + _Xx0 * (_Par0._Bvec[_Px + 1] - _Par0._Bvec[_Px]); } - discrete_distribution _Base_distribution; // TRANSITION, ABI: unused _Par subobject + discrete_distribution _Base_distribution; // TRANSITION, ABI: unused _Base_distribution._Par subobject param_type _Par; }; diff --git a/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp b/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp index 4e806f1d42d..f50fa9a8592 100644 --- a/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp +++ b/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp @@ -12,9 +12,9 @@ using namespace std; // GH-1600 ": piecewise_linear_distribution::param_type should not inherit from // discrete_distribution::param_type" -// Note that inheritance between distribution type should also be removed. +// Note that inheritance between distribution types should also be removed. template -void test_gh_1600_inheritance() { // COMPILE-ONLY +void test_gh_1600_inheritance() { STATIC_ASSERT(!is_base_of_v, piecewise_constant_distribution>); STATIC_ASSERT(!is_base_of_v::param_type, typename piecewise_constant_distribution::param_type>); @@ -30,11 +30,11 @@ void test_gh_1600_inheritance() { // COMPILE-ONLY // Same for corresponding param_type subobjects. // In vNext, we should probably remove unused members, so the sizes will be reduced. template -void test_gh_1600_abi() { // COMPILE-ONLY +void test_gh_1600_abi() { // piecewise_{constant,linear}_distribution and their param_type don't introduce padding bytes under MSVC ABI. // So it's OK to just add the sizes. - // The sizes will probably reduced in vNext. + // The sizes will be probably reduced in vNext. STATIC_ASSERT( sizeof(piecewise_constant_distribution) @@ -57,7 +57,7 @@ void test_gh_1600_abi() { // COMPILE-ONLY STATIC_ASSERT(alignof(typename piecewise_linear_distribution::param_type) == alignof(void*)); } -void test() { // COMPILE-ONLY +void test() { test_gh_1600_inheritance(); test_gh_1600_inheritance(); test_gh_1600_inheritance(); From d2168320a2cb5c2d7d91c8c5190de9a41049a164 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Thu, 22 Jan 2026 11:17:30 +0800 Subject: [PATCH 3/7] Decompose `_Base_params` in `piecewise_linear_distribution::param_type` --- stl/inc/random | 143 ++++++++++++------ .../test.compile.pass.cpp | 4 +- 2 files changed, 101 insertions(+), 46 deletions(-) diff --git a/stl/inc/random b/stl/inc/random index 893c7f76d87..6871971f178 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -4175,14 +4175,23 @@ public: void reset() noexcept /* strengthened */ {} // clear internal state + // reused by piecewise_{constant,linear}_distribution + template + _NODISCARD static result_type _Invoke_param_pcdf(_Engine& _Eng, const _Myvec& _Pcdf) { + const double _Px = _STD _Nrand_impl(_Eng); + const auto _First = _Pcdf.begin(); + const auto _Position = _STD lower_bound(_First, _STD _Prev_iter(_Pcdf.end()), _Px); + return static_cast(_Position - _First); + } + template _NODISCARD result_type operator()(_Engine& _Eng) _DISTRIBUTION_CONST { - return _Eval(_Eng, _Par); + return _Invoke_param_pcdf(_Eng, _Par._Pcdf); } template _NODISCARD result_type operator()(_Engine& _Eng, const param_type& _Par0) _DISTRIBUTION_CONST { - return _Eval(_Eng, _Par0); + return _Invoke_param_pcdf(_Eng, _Par0._Pcdf); } _NODISCARD friend bool operator==(const discrete_distribution& _Left, const discrete_distribution& _Right) { @@ -4207,16 +4216,6 @@ public: return _Dist._Par._Write(_Ostr); } -private: - template - result_type _Eval(_Engine& _Eng, const param_type& _Par0) const { - double _Px = _Nrand_impl(_Eng); - const auto _First = _Par0._Pcdf.begin(); - const auto _Position = _STD lower_bound(_First, _Prev_iter(_Par0._Pcdf.end()), _Px); - return static_cast(_Position - _First); - } - -public: param_type _Par; }; @@ -4398,12 +4397,12 @@ public: template result_type _Eval(_Engine& _Eng, const param_type& _Par0) _DISTRIBUTION_CONST { - size_t _Px = _Base_distribution(_Eng, _Par0._Base_params); + size_t _Px = discrete_distribution::_Invoke_param_pcdf(_Eng, _Par0._Base_params._Pcdf); uniform_real_distribution<_Ty> _Dist(_Par0._Bvec[_Px], _Par0._Bvec[_Px + 1]); return _Dist(_Eng); } - discrete_distribution _Base_distribution; // TRANSITION, ABI: unused _Base_distribution._Par subobject + discrete_distribution _Unused; // TRANSITION, ABI: this was a base class subobject param_type _Par; }; @@ -4417,15 +4416,17 @@ public: struct param_type { // parameter package using distribution_type = piecewise_linear_distribution; - param_type() : _Bvec{0, 1} { - _Base_params._Pvec.push_back(1.0); + param_type() { + _Init_base(); + _Pvec.push_back(1.0); + _Bvec = {0, 1}; } template - param_type(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2) : _Base_params(_Noinit), _Bvec(_First1, _Last1) { + param_type(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2) : _Bvec(_First1, _Last1) { if (2 <= _Bvec.size()) { for (size_t _Idx = 0; _Idx < _Bvec.size(); ++_Idx) { - _Base_params._Pvec.push_back(static_cast(*_First2++)); + _Pvec.push_back(static_cast(*_First2++)); } } else { // default construct _Bvec = {0, 1}; @@ -4435,12 +4436,12 @@ public: } template - param_type(initializer_list<_Ty> _Ilist, _Fn _Func) : _Base_params(_Noinit) { + param_type(initializer_list<_Ty> _Ilist, _Fn _Func) { if (2 <= _Ilist.size()) { _Bvec.assign(_Ilist); for (const auto& _Bval : _Bvec) { - _Base_params._Pvec.push_back(_Func(_Bval)); + _Pvec.push_back(_Func(_Bval)); } } else { // default construct _Bvec = {0, 1}; @@ -4450,7 +4451,7 @@ public: } template - param_type(size_t _Count, _Ty _Low, _Ty _High, _Fn _Func) : _Base_params(_Noinit) { + param_type(size_t _Count, _Ty _Low, _Ty _High, _Fn _Func) { _Ty _Range = _High - _Low; _STL_ASSERT(_Ty{0} < _Range, "invalid range for piecewise_linear_distribution"); if (_Count < 1) { @@ -4461,13 +4462,13 @@ public: for (size_t _Idx = 0; _Idx <= _Count; ++_Idx) { // compute _Bvec and _Pvec _Ty _Bval = _Low + _Idx * _Range; _Bvec.push_back(_Bval); - _Base_params._Pvec.push_back(_Func(_Bval)); + _Pvec.push_back(_Func(_Bval)); } _Init(); } _NODISCARD friend bool operator==(const param_type& _Left, const param_type& _Right) { - return _Left._Base_params == _Right._Base_params && _Left._Bvec == _Right._Bvec; + return _Left._Pvec == _Right._Pvec && _Left._Bvec == _Right._Bvec; } #if !_HAS_CXX20 @@ -4483,51 +4484,103 @@ public: #pragma warning(push) #pragma warning(disable : 4244) // '%s': conversion from '%s' to '%s', possible loss of data _NODISCARD vector<_Ty> densities() const { - vector<_Ty> _Ans(_Base_params._Pvec.begin(), _Base_params._Pvec.end()); + vector<_Ty> _Ans(_Pvec.begin(), _Pvec.end()); return _Ans; } #pragma warning(pop) _NODISCARD double _Piece_probability(const size_t _Idx) const { - return 0.5 * (_Base_params._Pvec[_Idx] + _Base_params._Pvec[_Idx + 1]) - * static_cast(_Bvec[_Idx + 1] - _Bvec[_Idx]); + return 0.5 * (_Pvec[_Idx] + _Pvec[_Idx + 1]) * static_cast(_Bvec[_Idx + 1] - _Bvec[_Idx]); + } + + void _Init_base(bool _Renorm = true) { // initialize like discrete_distribution::param_type + size_t _Size = _Pvec.size(); + size_t _Idx; + + if (_Renorm) { + if (_Pvec.empty()) { + _Pvec.push_back(1.0); // make empty vector degenerate + } else { // normalize probabilities + double _Sum = 0; + + for (_Idx = 0; _Idx < _Size; ++_Idx) { // sum all probabilities + _STL_ASSERT(0.0 <= _Pvec[_Idx], "invalid probability for piecewise_linear_distribution"); + _Sum += _Pvec[_Idx]; + } + + _STL_ASSERT(0.0 < _Sum, "invalid probability vector for piecewise_linear_distribution"); + if (_Sum != 1.0) { + for (_Idx = 0; _Idx < _Size; ++_Idx) { + _Pvec[_Idx] /= _Sum; + } + } + } + } + + _Pcdf.assign(1, _Pvec[0]); + for (_Idx = 1; _Idx < _Size; ++_Idx) { + _Pcdf.push_back(_Pvec[_Idx] + _Pcdf[_Idx - 1]); + } } void _Init(bool _Renorm = true) { // initialize - size_t _Size = _Base_params._Pvec.size(); + size_t _Size = _Pvec.size(); size_t _Idx; if (_Renorm) { - if (_Base_params._Pvec.empty()) { // make empty vector degenerate - _Base_params._Pvec = {1.0, 1.0}; + if (_Pvec.empty()) { // make empty vector degenerate + _Pvec = {1.0, 1.0}; } else { // normalize probabilities double _Sum = 0; - _STL_ASSERT(0.0 <= _Base_params._Pvec[0], "invalid probability for piecewise_linear_distribution"); + _STL_ASSERT(0.0 <= _Pvec[0], "invalid probability for piecewise_linear_distribution"); for (_Idx = 1; _Idx < _Size; ++_Idx) { // sum all probabilities - _STL_ASSERT( - 0.0 <= _Base_params._Pvec[_Idx], "invalid probability for piecewise_linear_distribution"); + _STL_ASSERT(0.0 <= _Pvec[_Idx], "invalid probability for piecewise_linear_distribution"); _Sum += _Piece_probability(_Idx - 1); } _STL_ASSERT(0.0 < _Sum, "invalid probability vector for piecewise_linear_distribution"); if (_Sum != 1.0) { for (_Idx = 0; _Idx < _Size; ++_Idx) { - _Base_params._Pvec[_Idx] /= _Sum; + _Pvec[_Idx] /= _Sum; } } } } - _Base_params._Pcdf.assign(1, _Piece_probability(0)); + _Pcdf.assign(1, _Piece_probability(0)); for (_Idx = 2; _Idx < _Size; ++_Idx) { - _Base_params._Pcdf.push_back(_Piece_probability(_Idx - 1) + _Base_params._Pcdf[_Idx - 2]); + _Pcdf.push_back(_Piece_probability(_Idx - 1) + _Pcdf[_Idx - 2]); } } - // TRANSITION, ABI: stores probability densities (N + 1 elements) in _Base_params._Pvec - // this breaks invariants of discrete_distribution::param_type - discrete_distribution::param_type _Base_params; + template + basic_istream<_Elem, _Traits>& _Read(basic_istream<_Elem, _Traits>& _Istr) { // read state from _Istr + size_t _Nvals; + _Istr >> _Nvals; + _Pvec.clear(); + for (; 0 < _Nvals; --_Nvals) { // get a value and add to vector + double _Val; + _In(_Istr, _Val); + _Pvec.push_back(_Val); + } + _Init_base(false); // don't renormalize, just compute CDF + return _Istr; + } + + template + basic_ostream<_Elem, _Traits>& _Write(basic_ostream<_Elem, _Traits>& _Ostr) const { // write state to _Ostr + _Ostr << ' ' << _Pvec.size(); + + for (const auto& _Val : _Pvec) { + _Out(_Ostr, _Val); + } + + return _Ostr; + } + + vector _Pvec; + vector _Pcdf; vector<_Ty> _Bvec; }; @@ -4595,10 +4648,10 @@ public: template friend basic_istream<_Elem, _Traits>& operator>>(basic_istream<_Elem, _Traits>& _Istr, piecewise_linear_distribution& _Dist) { // read state from _Istr - _Dist._Par._Base_params._Read(_Istr); + _Dist._Par._Read(_Istr); _Dist._Par._Bvec.clear(); - for (size_t _Idx = _Dist._Par._Base_params._Pvec.size(); 0 < _Idx; --_Idx) { + for (size_t _Idx = _Dist._Par._Pvec.size(); 0 < _Idx; --_Idx) { // get a value and add to intervals vector double _Val; _In(_Istr, _Val); @@ -4611,7 +4664,7 @@ public: template friend basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, const piecewise_linear_distribution& _Dist) { // write state to _Ostr - _Dist._Par._Base_params._Write(_Ostr); + _Dist._Par._Write(_Ostr); for (const auto& _Val : _Dist._Par._Bvec) { _Out(_Ostr, _Val); @@ -4622,9 +4675,9 @@ public: template result_type _Eval(_Engine& _Eng, const param_type& _Par0) _DISTRIBUTION_CONST { - size_t _Px = _Base_distribution(_Eng, _Par0._Base_params); - double _Px0 = _Par0._Base_params._Pvec[_Px]; - double _Px1 = _Par0._Base_params._Pvec[_Px + 1]; + size_t _Px = discrete_distribution::_Invoke_param_pcdf(_Eng, _Par0._Pcdf); + double _Px0 = _Par0._Pvec[_Px]; + double _Px1 = _Par0._Pvec[_Px + 1]; uniform_real_distribution<_Ty> _Dist; result_type _Xx0 = _Dist(_Eng); @@ -4636,7 +4689,7 @@ public: return _Par0._Bvec[_Px] + _Xx0 * (_Par0._Bvec[_Px + 1] - _Par0._Bvec[_Px]); } - discrete_distribution _Base_distribution; // TRANSITION, ABI: unused _Base_distribution._Par subobject + discrete_distribution _Unused; // TRANSITION, ABI: this was a base class subobject param_type _Par; }; diff --git a/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp b/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp index f50fa9a8592..03525add5e5 100644 --- a/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp +++ b/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp @@ -27,7 +27,9 @@ void test_gh_1600_inheritance() { // Until 2026-01, piecewise_{constant,linear}_distribution were both derived from discrete_distribution. // Same for their param_type. // Now the discrete_distribution objects are changed from base class subobjects to leading member subobjects. -// Same for corresponding param_type subobjects. +// Same for piecewise_constant_distribution::param_type. +// For piecewise_linear_distribution::param_type, the original discrete_distribution::param_type subobject is +// decomposed into vectors to avoid imposing invariants. // In vNext, we should probably remove unused members, so the sizes will be reduced. template void test_gh_1600_abi() { From 3fd43e57884757908dd8095e21db903ef93fc2c9 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Jan 2026 05:04:06 -0800 Subject: [PATCH 4/7] Avoid abbreviations that interfere with searching. --- stl/inc/random | 2 +- .../GH_001600_random_inheritance/test.compile.pass.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stl/inc/random b/stl/inc/random index 6871971f178..d20a9428b8c 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -4175,7 +4175,7 @@ public: void reset() noexcept /* strengthened */ {} // clear internal state - // reused by piecewise_{constant,linear}_distribution + // reused by piecewise_constant_distribution and piecewise_linear_distribution template _NODISCARD static result_type _Invoke_param_pcdf(_Engine& _Eng, const _Myvec& _Pcdf) { const double _Px = _STD _Nrand_impl(_Eng); diff --git a/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp b/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp index 03525add5e5..467914f2460 100644 --- a/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp +++ b/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp @@ -24,8 +24,8 @@ void test_gh_1600_inheritance() { typename piecewise_linear_distribution::param_type>); } -// Until 2026-01, piecewise_{constant,linear}_distribution were both derived from discrete_distribution. -// Same for their param_type. +// Until 2026-01, piecewise_constant_distribution and piecewise_linear_distribution +// were both derived from discrete_distribution. Same for their param_type structs. // Now the discrete_distribution objects are changed from base class subobjects to leading member subobjects. // Same for piecewise_constant_distribution::param_type. // For piecewise_linear_distribution::param_type, the original discrete_distribution::param_type subobject is @@ -33,8 +33,8 @@ void test_gh_1600_inheritance() { // In vNext, we should probably remove unused members, so the sizes will be reduced. template void test_gh_1600_abi() { - // piecewise_{constant,linear}_distribution and their param_type don't introduce padding bytes under MSVC ABI. - // So it's OK to just add the sizes. + // piecewise_constant_distribution, piecewise_linear_distribution, and their param_type structs + // don't introduce padding bytes under MSVC ABI. So it's OK to just add the sizes. // The sizes will be probably reduced in vNext. From 5f480c67bcbafe5f12f8e8ec9ca6b245bb1f48ea Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Jan 2026 05:13:26 -0800 Subject: [PATCH 5/7] Extract aliases to improve test readability. --- .../test.compile.pass.cpp | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp b/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp index 467914f2460..31a454104bd 100644 --- a/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp +++ b/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp @@ -15,13 +15,19 @@ using namespace std; // Note that inheritance between distribution types should also be removed. template void test_gh_1600_inheritance() { - STATIC_ASSERT(!is_base_of_v, piecewise_constant_distribution>); - STATIC_ASSERT(!is_base_of_v::param_type, - typename piecewise_constant_distribution::param_type>); + using Con = piecewise_constant_distribution; + using Lin = piecewise_linear_distribution; + using Dis = discrete_distribution; - STATIC_ASSERT(!is_base_of_v, piecewise_linear_distribution>); - STATIC_ASSERT(!is_base_of_v::param_type, - typename piecewise_linear_distribution::param_type>); + using ConParam = typename Con::param_type; + using LinParam = typename Lin::param_type; + using DisParam = Dis::param_type; + + STATIC_ASSERT(!is_base_of_v); + STATIC_ASSERT(!is_base_of_v); + + STATIC_ASSERT(!is_base_of_v); + STATIC_ASSERT(!is_base_of_v); } // Until 2026-01, piecewise_constant_distribution and piecewise_linear_distribution @@ -38,25 +44,27 @@ void test_gh_1600_abi() { // The sizes will be probably reduced in vNext. - STATIC_ASSERT( - sizeof(piecewise_constant_distribution) - == sizeof(discrete_distribution) + sizeof(typename piecewise_constant_distribution::param_type)); - STATIC_ASSERT(sizeof(typename piecewise_constant_distribution::param_type) - == sizeof(discrete_distribution::param_type) + sizeof(vector)); + using Con = piecewise_constant_distribution; + using Lin = piecewise_linear_distribution; + using Dis = discrete_distribution; + + using ConParam = typename Con::param_type; + using LinParam = typename Lin::param_type; + using DisParam = Dis::param_type; + + STATIC_ASSERT(sizeof(Con) == sizeof(Dis) + sizeof(ConParam)); + STATIC_ASSERT(sizeof(ConParam) == sizeof(DisParam) + sizeof(vector)); - STATIC_ASSERT( - sizeof(piecewise_linear_distribution) - == sizeof(discrete_distribution) + sizeof(typename piecewise_linear_distribution::param_type)); - STATIC_ASSERT(sizeof(typename piecewise_linear_distribution::param_type) - == sizeof(discrete_distribution::param_type) + sizeof(vector)); + STATIC_ASSERT(sizeof(Lin) == sizeof(Dis) + sizeof(LinParam)); + STATIC_ASSERT(sizeof(LinParam) == sizeof(DisParam) + sizeof(vector)); // The alignments are likely to be unchanged in vNext. - STATIC_ASSERT(alignof(piecewise_constant_distribution) == alignof(void*)); - STATIC_ASSERT(alignof(typename piecewise_constant_distribution::param_type) == alignof(void*)); + STATIC_ASSERT(alignof(Con) == alignof(void*)); + STATIC_ASSERT(alignof(ConParam) == alignof(void*)); - STATIC_ASSERT(alignof(piecewise_linear_distribution) == alignof(void*)); - STATIC_ASSERT(alignof(typename piecewise_linear_distribution::param_type) == alignof(void*)); + STATIC_ASSERT(alignof(Lin) == alignof(void*)); + STATIC_ASSERT(alignof(LinParam) == alignof(void*)); } void test() { From 8aa353543a13d5a1744837ecfa33c56b78631f50 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Jan 2026 05:18:40 -0800 Subject: [PATCH 6/7] Grammar. --- .../tests/GH_001600_random_inheritance/test.compile.pass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp b/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp index 31a454104bd..977fa0c19b0 100644 --- a/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp +++ b/tests/std/tests/GH_001600_random_inheritance/test.compile.pass.cpp @@ -42,7 +42,7 @@ void test_gh_1600_abi() { // piecewise_constant_distribution, piecewise_linear_distribution, and their param_type structs // don't introduce padding bytes under MSVC ABI. So it's OK to just add the sizes. - // The sizes will be probably reduced in vNext. + // The sizes will probably be reduced in vNext. using Con = piecewise_constant_distribution; using Lin = piecewise_linear_distribution; From 99966f276abcc61afc0f9b490195b827ea9fc3cf Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 30 Jan 2026 05:27:42 -0800 Subject: [PATCH 7/7] Partially pre-existing: Add const, scope `_Idx`. --- stl/inc/random | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/stl/inc/random b/stl/inc/random index d20a9428b8c..1b558903ea9 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -4082,9 +4082,8 @@ public: return _Pvec; } - void _Init(bool _Renorm = true) { // initialize - size_t _Size = _Pvec.size(); - size_t _Idx; + void _Init(const bool _Renorm = true) { // initialize + const size_t _Size = _Pvec.size(); if (_Renorm) { if (_Pvec.empty()) { @@ -4092,14 +4091,14 @@ public: } else { // normalize probabilities double _Sum = 0; - for (_Idx = 0; _Idx < _Size; ++_Idx) { // sum all probabilities + for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { // sum all probabilities _STL_ASSERT(0.0 <= _Pvec[_Idx], "invalid probability for discrete_distribution"); _Sum += _Pvec[_Idx]; } _STL_ASSERT(0.0 < _Sum, "invalid probability vector for discrete_distribution"); if (_Sum != 1.0) { - for (_Idx = 0; _Idx < _Size; ++_Idx) { + for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { _Pvec[_Idx] /= _Sum; } } @@ -4107,7 +4106,7 @@ public: } _Pcdf.assign(1, _Pvec[0]); - for (_Idx = 1; _Idx < _Size; ++_Idx) { + for (size_t _Idx = 1; _Idx < _Size; ++_Idx) { _Pcdf.push_back(_Pvec[_Idx] + _Pcdf[_Idx - 1]); } } @@ -4493,9 +4492,8 @@ public: return 0.5 * (_Pvec[_Idx] + _Pvec[_Idx + 1]) * static_cast(_Bvec[_Idx + 1] - _Bvec[_Idx]); } - void _Init_base(bool _Renorm = true) { // initialize like discrete_distribution::param_type - size_t _Size = _Pvec.size(); - size_t _Idx; + void _Init_base(const bool _Renorm = true) { // initialize like discrete_distribution::param_type + const size_t _Size = _Pvec.size(); if (_Renorm) { if (_Pvec.empty()) { @@ -4503,14 +4501,14 @@ public: } else { // normalize probabilities double _Sum = 0; - for (_Idx = 0; _Idx < _Size; ++_Idx) { // sum all probabilities + for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { // sum all probabilities _STL_ASSERT(0.0 <= _Pvec[_Idx], "invalid probability for piecewise_linear_distribution"); _Sum += _Pvec[_Idx]; } _STL_ASSERT(0.0 < _Sum, "invalid probability vector for piecewise_linear_distribution"); if (_Sum != 1.0) { - for (_Idx = 0; _Idx < _Size; ++_Idx) { + for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { _Pvec[_Idx] /= _Sum; } } @@ -4518,14 +4516,13 @@ public: } _Pcdf.assign(1, _Pvec[0]); - for (_Idx = 1; _Idx < _Size; ++_Idx) { + for (size_t _Idx = 1; _Idx < _Size; ++_Idx) { _Pcdf.push_back(_Pvec[_Idx] + _Pcdf[_Idx - 1]); } } - void _Init(bool _Renorm = true) { // initialize - size_t _Size = _Pvec.size(); - size_t _Idx; + void _Init(const bool _Renorm = true) { // initialize + const size_t _Size = _Pvec.size(); if (_Renorm) { if (_Pvec.empty()) { // make empty vector degenerate @@ -4534,14 +4531,14 @@ public: double _Sum = 0; _STL_ASSERT(0.0 <= _Pvec[0], "invalid probability for piecewise_linear_distribution"); - for (_Idx = 1; _Idx < _Size; ++_Idx) { // sum all probabilities + for (size_t _Idx = 1; _Idx < _Size; ++_Idx) { // sum all probabilities _STL_ASSERT(0.0 <= _Pvec[_Idx], "invalid probability for piecewise_linear_distribution"); _Sum += _Piece_probability(_Idx - 1); } _STL_ASSERT(0.0 < _Sum, "invalid probability vector for piecewise_linear_distribution"); if (_Sum != 1.0) { - for (_Idx = 0; _Idx < _Size; ++_Idx) { + for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { _Pvec[_Idx] /= _Sum; } } @@ -4549,7 +4546,7 @@ public: } _Pcdf.assign(1, _Piece_probability(0)); - for (_Idx = 2; _Idx < _Size; ++_Idx) { + for (size_t _Idx = 2; _Idx < _Size; ++_Idx) { _Pcdf.push_back(_Piece_probability(_Idx - 1) + _Pcdf[_Idx - 2]); } }