]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Simplify std::__throw_bad_variant_access
authorJonathan Wakely <jwakely@redhat.com>
Tue, 22 Oct 2024 15:06:12 +0000 (16:06 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Thu, 24 Oct 2024 16:22:33 +0000 (17:22 +0100)
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 integer index
selecting one of the four possible string literals to use, ensuring
that the std::bad_variant_access constructor is only called with those
literals.

Passing an index 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 (__throw_bad_variant_access(unsigned)):
Define new function as inline friend, with namespace-scope
declaration using noreturn attribute.
(__throw_bad_variant_access(const char*)): Remove.
(__throw_bad_variant_access(bool)): Remove.
(visit, visit<R>): Adjust calls to __throw_bad_variant_access.

Reviewed-by: Patrick Palka <ppalka@redhat.com>
libstdc++-v3/include/std/variant

index cf532126d798b1fe8e1156ba291a4bb08995e5ef..bd0f9c3252a531b086b8466650c735f501e097be 100644 (file)
@@ -1402,6 +1402,8 @@ namespace __detail::__variant
                   && (is_swappable_v<_Types> && ...))>
     swap(variant<_Types...>&, variant<_Types...>&) = delete;
 
+  [[noreturn]] void __throw_bad_variant_access(unsigned);
+
   class bad_variant_access : public exception
   {
   public:
@@ -1411,28 +1413,24 @@ 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([[maybe_unused]] unsigned __n)
+    {
+      [[maybe_unused]] static constexpr 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[__n % 4u]));
+    }
   };
 
-  // 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)
-  {
-    if (__valueless) [[__unlikely__]]
-      __throw_bad_variant_access("std::get: variant is valueless");
-    else
-      __throw_bad_variant_access("std::get: wrong index for variant");
-  }
-
   template<typename... _Types>
     class variant
     : private __detail::__variant::_Variant_base<_Types...>,
@@ -1941,7 +1939,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 +1979,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))...);