[TySan] Don't report globals with incomplete types. (#121922)
[llvm-project.git] / libcxx / include / __format / format_arg_store.h
blob8b2c95c657c9bdc184459c57f28028bd3e2890f9
1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
10 #ifndef _LIBCPP___FORMAT_FORMAT_ARG_STORE_H
11 #define _LIBCPP___FORMAT_FORMAT_ARG_STORE_H
13 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
14 # pragma GCC system_header
15 #endif
17 #include <__concepts/arithmetic.h>
18 #include <__concepts/same_as.h>
19 #include <__config>
20 #include <__format/concepts.h>
21 #include <__format/format_arg.h>
22 #include <__type_traits/conditional.h>
23 #include <__type_traits/extent.h>
24 #include <__type_traits/remove_const.h>
25 #include <cstdint>
26 #include <string>
27 #include <string_view>
29 _LIBCPP_BEGIN_NAMESPACE_STD
31 #if _LIBCPP_STD_VER >= 20
33 namespace __format {
35 /// \returns The @c __arg_t based on the type of the formatting argument.
36 ///
37 /// \pre \c __formattable<_Tp, typename _Context::char_type>
38 template <class _Context, class _Tp>
39 consteval __arg_t __determine_arg_t();
41 // Boolean
42 template <class, same_as<bool> _Tp>
43 consteval __arg_t __determine_arg_t() {
44 return __arg_t::__boolean;
47 // Char
48 template <class _Context, same_as<typename _Context::char_type> _Tp>
49 consteval __arg_t __determine_arg_t() {
50 return __arg_t::__char_type;
52 # if _LIBCPP_HAS_WIDE_CHARACTERS
53 template <class _Context, class _CharT>
54 requires(same_as<typename _Context::char_type, wchar_t> && same_as<_CharT, char>)
55 consteval __arg_t __determine_arg_t() {
56 return __arg_t::__char_type;
58 # endif
60 // Signed integers
61 template <class, __libcpp_signed_integer _Tp>
62 consteval __arg_t __determine_arg_t() {
63 if constexpr (sizeof(_Tp) <= sizeof(int))
64 return __arg_t::__int;
65 else if constexpr (sizeof(_Tp) <= sizeof(long long))
66 return __arg_t::__long_long;
67 # if _LIBCPP_HAS_INT128
68 else if constexpr (sizeof(_Tp) == sizeof(__int128_t))
69 return __arg_t::__i128;
70 # endif
71 else
72 static_assert(sizeof(_Tp) == 0, "an unsupported signed integer was used");
75 // Unsigned integers
76 template <class, __libcpp_unsigned_integer _Tp>
77 consteval __arg_t __determine_arg_t() {
78 if constexpr (sizeof(_Tp) <= sizeof(unsigned))
79 return __arg_t::__unsigned;
80 else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long))
81 return __arg_t::__unsigned_long_long;
82 # if _LIBCPP_HAS_INT128
83 else if constexpr (sizeof(_Tp) == sizeof(__uint128_t))
84 return __arg_t::__u128;
85 # endif
86 else
87 static_assert(sizeof(_Tp) == 0, "an unsupported unsigned integer was used");
90 // Floating-point
91 template <class, same_as<float> _Tp>
92 consteval __arg_t __determine_arg_t() {
93 return __arg_t::__float;
95 template <class, same_as<double> _Tp>
96 consteval __arg_t __determine_arg_t() {
97 return __arg_t::__double;
99 template <class, same_as<long double> _Tp>
100 consteval __arg_t __determine_arg_t() {
101 return __arg_t::__long_double;
104 // Char pointer
105 template <class _Context, class _Tp>
106 requires(same_as<typename _Context::char_type*, _Tp> || same_as<const typename _Context::char_type*, _Tp>)
107 consteval __arg_t __determine_arg_t() {
108 return __arg_t::__const_char_type_ptr;
111 // Char array
112 template <class _Context, class _Tp>
113 requires(is_array_v<_Tp> && same_as<_Tp, typename _Context::char_type[extent_v<_Tp>]>)
114 consteval __arg_t __determine_arg_t() {
115 return __arg_t::__string_view;
118 // String view
119 template <class _Context, class _Tp>
120 requires(same_as<typename _Context::char_type, typename _Tp::value_type> &&
121 same_as<_Tp, basic_string_view<typename _Tp::value_type, typename _Tp::traits_type>>)
122 consteval __arg_t __determine_arg_t() {
123 return __arg_t::__string_view;
126 // String
127 template <class _Context, class _Tp>
128 requires(
129 same_as<typename _Context::char_type, typename _Tp::value_type> &&
130 same_as<_Tp, basic_string<typename _Tp::value_type, typename _Tp::traits_type, typename _Tp::allocator_type>>)
131 consteval __arg_t __determine_arg_t() {
132 return __arg_t::__string_view;
135 // Pointers
136 template <class, class _Ptr>
137 requires(same_as<_Ptr, void*> || same_as<_Ptr, const void*> || same_as<_Ptr, nullptr_t>)
138 consteval __arg_t __determine_arg_t() {
139 return __arg_t::__ptr;
142 // Handle
144 // Note this version can't be constrained avoiding ambiguous overloads.
145 // That means it can be instantiated by disabled formatters. To solve this, a
146 // constrained version for not formattable formatters is added.
147 template <class _Context, class _Tp>
148 consteval __arg_t __determine_arg_t() {
149 return __arg_t::__handle;
152 // The overload for not formattable types allows triggering the static
153 // assertion below.
154 template <class _Context, class _Tp>
155 requires(!__formattable_with<_Tp, _Context>)
156 consteval __arg_t __determine_arg_t() {
157 return __arg_t::__none;
160 // Pseudo constuctor for basic_format_arg
162 // Modeled after template<class T> explicit basic_format_arg(T& v) noexcept;
163 // [format.arg]/4-6
164 template <class _Context, class _Tp>
165 _LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp& __value) noexcept {
166 using _Dp = remove_const_t<_Tp>;
167 constexpr __arg_t __arg = __determine_arg_t<_Context, _Dp>();
168 static_assert(__arg != __arg_t::__none, "the supplied type is not formattable");
169 static_assert(__formattable_with<_Tp, _Context>);
171 // Not all types can be used to directly initialize the
172 // __basic_format_arg_value. First handle all types needing adjustment, the
173 // final else requires no adjustment.
174 if constexpr (__arg == __arg_t::__char_type)
176 # if _LIBCPP_HAS_WIDE_CHARACTERS
177 if constexpr (same_as<typename _Context::char_type, wchar_t> && same_as<_Dp, char>)
178 return basic_format_arg<_Context>{__arg, static_cast<wchar_t>(static_cast<unsigned char>(__value))};
179 else
180 # endif
181 return basic_format_arg<_Context>{__arg, __value};
182 else if constexpr (__arg == __arg_t::__int)
183 return basic_format_arg<_Context>{__arg, static_cast<int>(__value)};
184 else if constexpr (__arg == __arg_t::__long_long)
185 return basic_format_arg<_Context>{__arg, static_cast<long long>(__value)};
186 else if constexpr (__arg == __arg_t::__unsigned)
187 return basic_format_arg<_Context>{__arg, static_cast<unsigned>(__value)};
188 else if constexpr (__arg == __arg_t::__unsigned_long_long)
189 return basic_format_arg<_Context>{__arg, static_cast<unsigned long long>(__value)};
190 else if constexpr (__arg == __arg_t::__string_view)
191 // Using std::size on a character array will add the NUL-terminator to the size.
192 if constexpr (is_array_v<_Dp>)
193 return basic_format_arg<_Context>{
194 __arg, basic_string_view<typename _Context::char_type>{__value, extent_v<_Dp> - 1}};
195 else
196 // When the _Traits or _Allocator are different an implicit conversion will
197 // fail.
198 return basic_format_arg<_Context>{
199 __arg, basic_string_view<typename _Context::char_type>{__value.data(), __value.size()}};
200 else if constexpr (__arg == __arg_t::__ptr)
201 return basic_format_arg<_Context>{__arg, static_cast<const void*>(__value)};
202 else if constexpr (__arg == __arg_t::__handle)
203 return basic_format_arg<_Context>{__arg, typename __basic_format_arg_value<_Context>::__handle{__value}};
204 else
205 return basic_format_arg<_Context>{__arg, __value};
208 template <class _Context, class... _Args>
209 _LIBCPP_HIDE_FROM_ABI void
210 __create_packed_storage(uint64_t& __types, __basic_format_arg_value<_Context>* __values, _Args&... __args) noexcept {
211 int __shift = 0;
213 [&] {
214 basic_format_arg<_Context> __arg = __format::__create_format_arg<_Context>(__args);
215 if (__shift != 0)
216 __types |= static_cast<uint64_t>(__arg.__type_) << __shift;
217 else
218 // Assigns the initial value.
219 __types = static_cast<uint64_t>(__arg.__type_);
220 __shift += __packed_arg_t_bits;
221 *__values++ = __arg.__value_;
222 }(),
223 ...);
226 template <class _Context, class... _Args>
227 _LIBCPP_HIDE_FROM_ABI void __store_basic_format_arg(basic_format_arg<_Context>* __data, _Args&... __args) noexcept {
228 ([&] { *__data++ = __format::__create_format_arg<_Context>(__args); }(), ...);
231 template <class _Context, size_t _Np>
232 struct __packed_format_arg_store {
233 __basic_format_arg_value<_Context> __values_[_Np];
234 uint64_t __types_ = 0;
237 template <class _Context>
238 struct __packed_format_arg_store<_Context, 0> {
239 uint64_t __types_ = 0;
242 template <class _Context, size_t _Np>
243 struct __unpacked_format_arg_store {
244 basic_format_arg<_Context> __args_[_Np];
247 } // namespace __format
249 template <class _Context, class... _Args>
250 struct _LIBCPP_TEMPLATE_VIS __format_arg_store {
251 _LIBCPP_HIDE_FROM_ABI __format_arg_store(_Args&... __args) noexcept {
252 if constexpr (sizeof...(_Args) != 0) {
253 if constexpr (__format::__use_packed_format_arg_store(sizeof...(_Args)))
254 __format::__create_packed_storage(__storage.__types_, __storage.__values_, __args...);
255 else
256 __format::__store_basic_format_arg<_Context>(__storage.__args_, __args...);
260 using _Storage =
261 conditional_t<__format::__use_packed_format_arg_store(sizeof...(_Args)),
262 __format::__packed_format_arg_store<_Context, sizeof...(_Args)>,
263 __format::__unpacked_format_arg_store<_Context, sizeof...(_Args)>>;
265 _Storage __storage;
268 #endif // _LIBCPP_STD_VER >= 20
270 _LIBCPP_END_NAMESPACE_STD
272 #endif // _LIBCPP___FORMAT_FORMAT_ARG_STORE_H