This removes the overload of __throw_bad_variant_access that must be called with a string literal. This avoids a potential source of undefined behaviour if that function got misused. The other overload that takes a bool parameter can be adjusted to take an int describing which of the four possible string literals to use, and ensure that the std::bad_variant_access constructor is only called with those. Passing an int outside the range [0,3] is bogus, but will still select a valid string literal and avoid undefined behaviour.
libstdc++-v3/ChangeLog: * include/std/variant (bad_variant_access): Adjust friend declaration. (__throw_bad_variant_access(const char*)): Remove. (__throw_bad_variant_access(bool)): Change parameter to int. (visit, visit<R>): Adjust calls to __throw_bad_variant_access. --- Tested x86_64-linux. Also available for review at https://forge.sourceware.org/gcc/gcc-TEST/pulls/3 libstdc++-v3/include/std/variant | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant index cf532126d79..d79299be634 100644 --- a/libstdc++-v3/include/std/variant +++ b/libstdc++-v3/include/std/variant @@ -1411,26 +1411,25 @@ namespace __detail::__variant { return _M_reason; } private: + // Must only be called with a string literal bad_variant_access(const char* __reason) noexcept : _M_reason(__reason) { } // Must point to a string with static storage duration: const char* _M_reason = "bad variant access"; - friend void __throw_bad_variant_access(const char* __what); + friend void __throw_bad_variant_access(int); }; - // Must only be called with a string literal inline void - __throw_bad_variant_access(const char* __what) - { _GLIBCXX_THROW_OR_ABORT(bad_variant_access(__what)); } - - inline void - __throw_bad_variant_access(bool __valueless) + __throw_bad_variant_access(int __reason) { - if (__valueless) [[__unlikely__]] - __throw_bad_variant_access("std::get: variant is valueless"); - else - __throw_bad_variant_access("std::get: wrong index for variant"); + const char* __reasons[] = { + "std::get: wrong index for variant", + "std::get: variant is valueless", + "std::visit: variant is valueless", + "std::visit<R>: variant is valueless" + }; + _GLIBCXX_THROW_OR_ABORT(bad_variant_access(__reasons[__reason % 4u])); } template<typename... _Types> @@ -1941,7 +1940,7 @@ namespace __detail::__variant namespace __variant = std::__detail::__variant; if ((__variant::__as(__variants).valueless_by_exception() || ...)) - __throw_bad_variant_access("std::visit: variant is valueless"); + __throw_bad_variant_access(2); using _Result_type = __detail::__variant::__visit_result_t<_Visitor, _Variants...>; @@ -1981,7 +1980,7 @@ namespace __detail::__variant namespace __variant = std::__detail::__variant; if ((__variant::__as(__variants).valueless_by_exception() || ...)) - __throw_bad_variant_access("std::visit<R>: variant is valueless"); + __throw_bad_variant_access(3); return std::__do_visit<_Res>(std::forward<_Visitor>(__visitor), __variant::__as(std::forward<_Variants>(__variants))...); -- 2.46.2