diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv --- a/libcxx/docs/Status/Cxx20Issues.csv +++ b/libcxx/docs/Status/Cxx20Issues.csv @@ -275,7 +275,7 @@ "`3364 `__","Initialize data members of ranges and their iterators","Prague","","" "`3367 `__","Integer-class conversions should not throw","Prague","","" "`3369 `__","``span``\ 's deduction-guide for built-in arrays doesn't work","Prague","","" -"`3371 `__","``visit_format_arg``\ and ``make_format_args``\ are not hidden friends","Prague","","" +"`3371 `__","``visit_format_arg``\ and ``make_format_args``\ are not hidden friends","Prague","|Complete|","14.0" "`3372 `__","``vformat_to``\ should not try to deduce ``Out``\ twice","Prague","","" "`3373 `__","``{to,from}_chars_result``\ and ``format_to_n_result``\ need the ""we really mean what we say"" wording","Prague","","" "`3374 `__","P0653 + P1006 should have made the other ``std::to_address``\ overload ``constexpr``\ ","Prague","|Complete|","12.0" diff --git a/libcxx/docs/Status/Format.rst b/libcxx/docs/Status/Format.rst --- a/libcxx/docs/Status/Format.rst +++ b/libcxx/docs/Status/Format.rst @@ -39,10 +39,6 @@ (Please mark all Format-related TODO comments with the string ``TODO FMT``, so we can find them easily.) - * C++23 may break the ABI with `P2216 `_. - This ABI break may be backported to C++20. Therefore the library will not - be available on platforms where the ABI break is an issue. - Paper and Issue Status ====================== diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -128,7 +128,11 @@ __config __debug __errc + __format/format_arg.h + __format/format_args.h + __format/format_context.h __format/format_error.h + __format/format_fwd.h __format/format_parse_context.h __function_like.h __functional_base diff --git a/libcxx/include/__availability b/libcxx/include/__availability --- a/libcxx/include/__availability +++ b/libcxx/include/__availability @@ -139,9 +139,9 @@ // # define _LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_semaphore // This controls the availability of the C++20 format library. - // The library is in development and not ABI stable yet. Currently - // P2216 is aiming to be retroactively accepted in C++20. This paper - // contains ABI breaking changes. + // The library is in development and not ABI stable yet. P2216 is + // retroactively accepted in C++20. This paper contains ABI breaking + // changes. # define _LIBCPP_AVAILABILITY_FORMAT // # define _LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format @@ -238,9 +238,9 @@ # endif // This controls the availability of the C++20 format library. - // The library is in development and not ABI stable yet. Currently - // P2216 is aiming to be retroactively accepted in C++20. This paper - // contains ABI breaking changes. + // The library is in development and not ABI stable yet. P2216 is + // retroactively accepted in C++20. This paper contains ABI breaking + // changes. # define _LIBCPP_AVAILABILITY_FORMAT \ __attribute__((unavailable)) # define _LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format diff --git a/libcxx/include/__format/format_arg.h b/libcxx/include/__format/format_arg.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__format/format_arg.h @@ -0,0 +1,256 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___FORMAT_FORMAT_ARG_H +#define _LIBCPP___FORMAT_FORMAT_ARG_H + +#include <__config> +#include <__format/format_error.h> +#include <__format/format_fwd.h> +#include <__functional_base> +#include <__variant/monostate.h> +#include +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +namespace __format { +/** The type stored in @ref basic_format_arg. */ +enum class _LIBCPP_ENUM_VIS __arg_t : uint8_t { + __none, + __bool, + __char_type, + __int, + __long_long, +#ifndef _LIBCPP_HAS_NO_INT128 + __i128, +#endif + __unsigned, + __unsigned_long_long, +#ifndef _LIBCPP_HAS_NO_INT128 + __u128, +#endif + __float, + __double, + __long_double, + __const_char_type_ptr, + __string_view, + __ptr +}; +} // namespace __format + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT decltype(auto) +visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) { + switch (__arg.__type_) { + case __format::__arg_t::__none: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), monostate{}); + case __format::__arg_t::__bool: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__bool); + case __format::__arg_t::__char_type: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__char_type); + case __format::__arg_t::__int: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__int); + case __format::__arg_t::__long_long: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__long_long); +#ifndef _LIBCPP_HAS_NO_INT128 + case __format::__arg_t::__i128: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__i128); +#endif + case __format::__arg_t::__unsigned: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__unsigned); + case __format::__arg_t::__unsigned_long_long: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), + __arg.__unsigned_long_long); +#ifndef _LIBCPP_HAS_NO_INT128 + case __format::__arg_t::__u128: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__u128); +#endif + case __format::__arg_t::__float: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__float); + case __format::__arg_t::__double: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__double); + case __format::__arg_t::__long_double: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__long_double); + case __format::__arg_t::__const_char_type_ptr: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), + __arg.__const_char_type_ptr); + case __format::__arg_t::__string_view: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__string_view); + case __format::__arg_t::__ptr: + return _VSTD::invoke(_VSTD::forward<_Visitor>(__vis), __arg.__ptr); + } + _LIBCPP_UNREACHABLE(); +} + +template +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg { +public: + // TODO FMT Define the handle class. + class handle; + + _LIBCPP_HIDE_FROM_ABI basic_format_arg() noexcept + : __type_{__format::__arg_t::__none} {} + + _LIBCPP_HIDE_FROM_ABI explicit operator bool() const noexcept { + return __type_ != __format::__arg_t::__none; + } + +private: + using char_type = typename _Context::char_type; + + // TODO FMT Implement constrain [format.arg]/4 + // Constraints: The template specialization + // typename Context::template formatter_type + // meets the Formatter requirements ([formatter.requirements]). The extent + // to which an implementation determines that the specialization meets the + // Formatter requirements is unspecified, except that as a minimum the + // expression + // typename Context::template formatter_type() + // .format(declval(), declval()) + // shall be well-formed when treated as an unevaluated operand. + + template + _LIBCPP_HIDE_FROM_ABI + _LIBCPP_AVAILABILITY_FORMAT friend __format_arg_store<_Ctx, _Args...> + _VSTD::make_format_args(const _Args&...); + + template + _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT friend decltype(auto) + _VSTD::visit_format_arg(_Visitor&& __vis, basic_format_arg<_Ctx> __arg); + + union { + bool __bool; + char_type __char_type; + int __int; + unsigned __unsigned; + long long __long_long; + unsigned long long __unsigned_long_long; +#ifndef _LIBCPP_HAS_NO_INT128 + __int128_t __i128; + __uint128_t __u128; +#endif + float __float; + double __double; + long double __long_double; + const char_type* __const_char_type_ptr; + basic_string_view __string_view; + const void* __ptr; + // TODO FMT Add the handle. + }; + __format::__arg_t __type_; + + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(bool __v) noexcept + : __bool(__v), __type_(__format::__arg_t::__bool) {} + + template + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(_Tp __v) noexcept + requires(same_as<_Tp, char_type> || + (same_as<_Tp, char> && same_as)) + : __char_type(__v), __type_(__format::__arg_t::__char_type) {} + + template <__libcpp_signed_integer _Tp> + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(_Tp __v) noexcept { + if constexpr (sizeof(_Tp) <= sizeof(int)) { + __int = static_cast(__v); + __type_ = __format::__arg_t::__int; + } else if constexpr (sizeof(_Tp) <= sizeof(long long)) { + __long_long = static_cast(__v); + __type_ = __format::__arg_t::__long_long; + } +#ifndef _LIBCPP_HAS_NO_INT128 + else if constexpr (sizeof(_Tp) == sizeof(__int128_t)) { + __i128 = __v; + __type_ = __format::__arg_t::__i128; + } +#endif + else + static_assert(sizeof(_Tp) == 0, "An unsupported signed integer was used"); + } + + template <__libcpp_unsigned_integer _Tp> + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(_Tp __v) noexcept { + if constexpr (sizeof(_Tp) <= sizeof(unsigned)) { + __unsigned = static_cast(__v); + __type_ = __format::__arg_t::__unsigned; + } else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long)) { + __unsigned_long_long = static_cast(__v); + __type_ = __format::__arg_t::__unsigned_long_long; + } +#ifndef _LIBCPP_HAS_NO_INT128 + else if constexpr (sizeof(_Tp) == sizeof(__int128_t)) { + __u128 = __v; + __type_ = __format::__arg_t::__u128; + } +#endif + else + static_assert(sizeof(_Tp) == 0, + "An unsupported unsigned integer was used"); + } + + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(float __v) noexcept + : __float(__v), __type_(__format::__arg_t::__float) {} + + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(double __v) noexcept + : __double(__v), __type_(__format::__arg_t::__double) {} + + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(long double __v) noexcept + : __long_double(__v), __type_(__format::__arg_t::__long_double) {} + + // Note not a 'noexcept' function. + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg(const char_type* __s) + : __const_char_type_ptr(__s), + __type_(__format::__arg_t::__const_char_type_ptr) { + _LIBCPP_ASSERT(__s, "Used a nullptr argument to initialize a C-string"); + } + + template + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg( + basic_string_view __s) noexcept + : __string_view{__s.data(), __s.size()}, + __type_(__format::__arg_t::__string_view) {} + + template + _LIBCPP_HIDE_FROM_ABI explicit basic_format_arg( + const basic_string& __s) noexcept + : __string_view{__s.data(), __s.size()}, + __type_(__format::__arg_t::__string_view) {} + + _LIBCPP_HIDE_FROM_ABI + explicit basic_format_arg(nullptr_t) noexcept + : __ptr(nullptr), __type_(__format::__arg_t::__ptr) {} + + // TODO FMT Implement the _Tp* constructor. +}; + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMAT_ARG_H diff --git a/libcxx/include/__format/format_args.h b/libcxx/include/__format/format_args.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__format/format_args.h @@ -0,0 +1,71 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___FORMAT_FORMAT_ARGS_H +#define _LIBCPP___FORMAT_FORMAT_ARGS_H + +#include <__availability> +#include <__config> +#include <__format/format_fwd.h> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +template +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_args { +public: + // TODO FMT Implement [format.args]/5 + // [Note 1: Implementations are encouraged to optimize the representation of + // basic_format_args for small number of formatting arguments by storing + // indices of type alternatives separately from values and packing the + // former. - end note] + // Note: Change __format_arg_store to use a built-in array. + _LIBCPP_HIDE_FROM_ABI basic_format_args() noexcept = default; + + template + _LIBCPP_HIDE_FROM_ABI basic_format_args( + const __format_arg_store<_Context, _Args...>& __store) noexcept + : __size_(sizeof...(_Args)), __data_(__store.__args.data()) {} + + _LIBCPP_HIDE_FROM_ABI + basic_format_arg<_Context> get(size_t __id) const noexcept { + return __id < __size_ ? __data_[__id] : basic_format_arg<_Context>{}; + } + + _LIBCPP_HIDE_FROM_ABI size_t __size() const noexcept { return __size_; } + +private: + size_t __size_{0}; + const basic_format_arg<_Context>* __data_{nullptr}; +}; + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMAT_ARGS_H diff --git a/libcxx/include/__format/format_context.h b/libcxx/include/__format/format_context.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__format/format_context.h @@ -0,0 +1,160 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___FORMAT_FORMAT_CONTEXT_H +#define _LIBCPP___FORMAT_FORMAT_CONTEXT_H + +#include <__availability> +#include <__config> +#include <__format/format_args.h> +#include <__format/format_fwd.h> +#include <__iterator/concepts.h> +#include +#include +#include + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#include +#endif + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +template +requires output_iterator<_OutIt, const _CharT&> +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_context; + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +/** + * Helper to create a basic_format_context. + * + * This is needed since the constructor is private. + */ +template +_LIBCPP_HIDE_FROM_ABI basic_format_context<_OutIt, _CharT> +__format_context_create( + _OutIt __out_it, + basic_format_args> __args, + optional<_VSTD::locale>&& __loc = nullopt) { + return _VSTD::basic_format_context(_VSTD::move(__out_it), __args, + _VSTD::move(__loc)); +} +#else +template +_LIBCPP_HIDE_FROM_ABI basic_format_context<_OutIt, _CharT> +__format_context_create( + _OutIt __out_it, + basic_format_args> __args) { + return _VSTD::basic_format_context(_VSTD::move(__out_it), __args); +} +#endif + +template +requires output_iterator<_OutIt, const _CharT&> +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_context { +public: + using iterator = _OutIt; + using char_type = _CharT; + template + using formatter_type = formatter<_Tp, _CharT>; + + basic_format_context(const basic_format_context&) = delete; + basic_format_context& operator=(const basic_format_context&) = delete; + + _LIBCPP_HIDE_FROM_ABI basic_format_arg + arg(size_t __id) const { + return __args_.get(__id); + } +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + _LIBCPP_HIDE_FROM_ABI _VSTD::locale locale() { + if (!__loc_) + __loc_ = _VSTD::locale{}; + return *__loc_; + } +#endif + _LIBCPP_HIDE_FROM_ABI iterator out() { return __out_it_; } + _LIBCPP_HIDE_FROM_ABI void advance_to(iterator __it) { __out_it_ = __it; } + +private: + iterator __out_it_; + basic_format_args __args_; +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + + // The Standard doesn't specify how the locale is stored. + // [format.context]/6 + // std::locale locale(); + // Returns: The locale passed to the formatting function if the latter + // takes one, and std::locale() otherwise. + // This is done by storing the locale of the constructor in this optional. If + // locale() is called and the optional has no value the value will be created. + // This allows the implementation to lazily create the locale. + // TODO FMT Validate whether lazy creation is the best solution. + optional<_VSTD::locale> __loc_; + + template + friend _LIBCPP_HIDE_FROM_ABI basic_format_context<__OutIt, __CharT> + _VSTD::__format_context_create( + __OutIt, basic_format_args>, + optional<_VSTD::locale>&&); + + // Note: the Standard doesn't specify the required constructors. + _LIBCPP_HIDE_FROM_ABI + explicit basic_format_context(_OutIt __out_it, + basic_format_args __args, + optional<_VSTD::locale>&& __loc) + : __out_it_(_VSTD::move(__out_it)), __args_(__args), + __loc_(_VSTD::move(__loc)) {} +#else + template + friend _LIBCPP_HIDE_FROM_ABI basic_format_context<__OutIt, __CharT> + _VSTD::__format_context_create( + __OutIt, basic_format_args>); + + _LIBCPP_HIDE_FROM_ABI + explicit basic_format_context(_OutIt __out_it, + basic_format_args __args) + : __out_it_(_VSTD::move(__out_it)), __args_(__args) {} +#endif +}; + +// TODO FMT Implement [format.context]/4 +// [Note 1: For a given type charT, implementations are encouraged to provide a +// single instantiation of basic_format_context for appending to +// basic_string, vector, or any other container with contiguous +// storage by wrapping those in temporary objects with a uniform interface +// (such as a span) and polymorphic reallocation. - end note] + +using format_context = basic_format_context, char>; +using wformat_context = + basic_format_context, wchar_t>; + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMAT_CONTEXT_H diff --git a/libcxx/include/__format/format_fwd.h b/libcxx/include/__format/format_fwd.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__format/format_fwd.h @@ -0,0 +1,56 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___FORMAT_FORMAT_FWD_H +#define _LIBCPP___FORMAT_FORMAT_FWD_H + +#include <__availability> +#include <__config> +#include <__iterator/concepts.h> +#include <__utility/forward.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +template +class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg; + +template +struct _LIBCPP_TEMPLATE_VIS __format_arg_store; + +template +_LIBCPP_HIDE_FROM_ABI __format_arg_store<_Ctx, _Args...> +make_format_args(const _Args&...); + +template +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter; + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) + +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___FORMAT_FORMAT_FWD_H diff --git a/libcxx/include/concepts b/libcxx/include/concepts --- a/libcxx/include/concepts +++ b/libcxx/include/concepts @@ -152,10 +152,28 @@ #include <__concepts/swappable.h> #include <__concepts/totally_ordered.h> #include <__config> +#include #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) #pragma GCC system_header #endif +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS) + +// Concept helpers for the internal type traits for the fundamental types. + +template +concept __libcpp_unsigned_integer = __libcpp_is_unsigned_integer<_Tp>::value; +template +concept __libcpp_signed_integer = __libcpp_is_signed_integer<_Tp>::value; +template +concept __libcpp_floating_point = __libcpp_is_floating_point<_Tp>::value; + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS) + +_LIBCPP_END_NAMESPACE_STD + #endif // _LIBCPP_CONCEPTS diff --git a/libcxx/include/format b/libcxx/include/format --- a/libcxx/include/format +++ b/libcxx/include/format @@ -13,6 +13,136 @@ /* namespace std { + // [format.context], class template basic_format_context + template + class basic_format_context { + basic_format_args args_; // exposition only + Out out_; // exposition only + + public: + using iterator = Out; + using char_type = charT; + template using formatter_type = formatter; + + basic_format_arg arg(size_t id) const; + std::locale locale(); + + iterator out(); + void advance_to(iterator it); + }; + using format_context = basic_format_context; + using wformat_context = basic_format_context; + + // [format.args], class template basic_format_args + template + class basic_format_args { + size_t size_; // exposition only + const basic_format_arg* data_; // exposition only + + public: + basic_format_args() noexcept; + + template + basic_format_args(const format-arg-store& store) noexcept; + + basic_format_arg get(size_t i) const noexcept; + }; + using format_args = basic_format_args; + using wformat_args = basic_format_args; + + + template + using format_args_t = basic_format_args>; + + // [format.parse.ctx], class template basic_format_parse_context + template + class basic_format_parse_context { + public: + using char_type = charT; + using const_iterator = typename basic_string_view::const_iterator; + using iterator = const_iterator; + + private: + iterator begin_; // exposition only + iterator end_; // exposition only + enum indexing { unknown, manual, automatic }; // exposition only + indexing indexing_; // exposition only + size_t next_arg_id_; // exposition only + size_t num_args_; // exposition only + + public: + constexpr explicit basic_format_parse_context(basic_string_view fmt, + size_t num_args = 0) noexcept; + basic_format_parse_context(const basic_format_parse_context&) = delete; + basic_format_parse_context& operator=(const basic_format_parse_context&) = delete; + + constexpr const_iterator begin() const noexcept; + constexpr const_iterator end() const noexcept; + constexpr void advance_to(const_iterator it); + + constexpr size_t next_arg_id(); + constexpr void check_arg_id(size_t id); + }; + using format_parse_context = basic_format_parse_context; + using wformat_parse_context = basic_format_parse_context; + + // [format.arguments], arguments + // [format.arg], class template basic_format_arg + template + class basic_format_arg { + public: + class handle; + + private: + using char_type = typename Context::char_type; // exposition only + + variant, + const void*, handle> value; // exposition only + + template explicit basic_format_arg(const T& v) noexcept; // exposition only + explicit basic_format_arg(float n) noexcept; // exposition only + explicit basic_format_arg(double n) noexcept; // exposition only + explicit basic_format_arg(long double n) noexcept; // exposition only + explicit basic_format_arg(const char_type* s); // exposition only + + template + explicit basic_format_arg( + basic_string_view s) noexcept; // exposition only + + template + explicit basic_format_arg( + const basic_string& s) noexcept; // exposition only + + explicit basic_format_arg(nullptr_t) noexcept; // exposition only + + template + explicit basic_format_arg(const T* p) noexcept; // exposition only + + public: + basic_format_arg() noexcept; + + explicit operator bool() const noexcept; + }; + + template + see below visit_format_arg(Visitor&& vis, basic_format_arg arg); + + // [format.arg.store], class template format-arg-store + template + struct format-arg-store { // exposition only + array, sizeof...(Args)> args; + }; + + template + format-arg-store + make_format_args(const Args&... args); + template + format-arg-store + make_wformat_args(const Args&... args); + // [format.error], class format_error class format_error : public runtime_error { public: @@ -61,13 +191,61 @@ #if !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT) #include <__config> +#include <__format/format_arg.h> +#include <__format/format_args.h> +#include <__format/format_context.h> #include <__format/format_error.h> #include <__format/format_parse_context.h> +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) -# pragma GCC system_header +#pragma GCC system_header #endif +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#if !defined(_LIBCPP_HAS_NO_CONCEPTS) + +// TODO FMT Evaluate which templates should be external templates. This +// improves the efficiency of the header. However since the header is still +// under heavy development and not all classes are stable it makes no sense +// to do this optimization now. + +using format_args = basic_format_args; +using wformat_args = basic_format_args; + +template +using format_args_t = basic_format_args>; + +template +struct _LIBCPP_TEMPLATE_VIS __format_arg_store { + // TODO FMT Use a built-in array. + array, sizeof...(_Args)> __args; +}; + +template +_LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...> +make_format_args(const _Args&... __args) { + return {basic_format_arg<_Context>(__args)...}; +} + +template +_LIBCPP_HIDE_FROM_ABI __format_arg_store +make_wformat_args(const _Args&... __args) { + return _VSTD::make_format_args(__args...); +} + +#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS) +#endif //_LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + #endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT) #endif // _LIBCPP_FORMAT diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -431,7 +431,15 @@ export * module __format { + module format_arg { private header "__format/format_arg.h" } + module format_args { private header "__format/format_args.h" } + module format_context { + private header "__format/format_context.h" + export optional + export locale + } module format_error { private header "__format/format_error.h" } + module format_fwd { private header "__format/format_fwd.h" } module format_parse_context { private header "__format/format_parse_context.h" } } } diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -790,6 +790,7 @@ #endif // __has_keyword(__is_integral) // __libcpp_is_signed_integer, __libcpp_is_unsigned_integer +// implements __libcpp_signed_integer, __libcpp_unsigned_integer // [basic.fundamental] defines five standard signed integer types; // __int128_t is an extended signed integer type. @@ -817,6 +818,7 @@ #endif // is_floating_point +// implements __libcpp_floating_point template struct __libcpp_is_floating_point : public false_type {}; template <> struct __libcpp_is_floating_point : public true_type {}; diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/format/format_arg.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/format/format_arg.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/format/format_arg.module.verify.cpp @@ -0,0 +1,16 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__format/format_arg.h'}} +#include <__format/format_arg.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/format/format_args.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/format/format_args.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/format/format_args.module.verify.cpp @@ -0,0 +1,16 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__format/format_args.h'}} +#include <__format/format_args.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/format/format_context.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/format/format_context.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/format/format_context.module.verify.cpp @@ -0,0 +1,16 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__format/format_context.h'}} +#include <__format/format_context.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/format/format_fwd.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/format/format_fwd.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/format/format_fwd.module.verify.cpp @@ -0,0 +1,16 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__format/format_fwd.h'}} +#include <__format/format_fwd.h> diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/class.pass.cpp @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// + +// template +// struct format-arg-store { // exposition only +// array, sizeof...(Args)> args; +// }; +// +// Note more testing is done in the unit test for: +// template +// see below visit_format_arg(Visitor&& vis, basic_format_arg arg); + +#include +#include +#include + +#include "test_macros.h" + +template +void test() { + using Context = std::basic_format_context; + { + auto store = std::make_format_args(); + LIBCPP_STATIC_ASSERT( + std::is_same_v>); + LIBCPP_STATIC_ASSERT( + std::is_same_v, 0>>); + LIBCPP_ASSERT(store.__args.size() == 0); + } + { + auto store = std::make_format_args(1); + LIBCPP_STATIC_ASSERT( + std::is_same_v>); + LIBCPP_STATIC_ASSERT( + std::is_same_v, 1>>); + LIBCPP_ASSERT(store.__args.size() == 1); + } + { + auto store = std::make_format_args(1, 'c'); + LIBCPP_STATIC_ASSERT( + std::is_same_v>); + LIBCPP_STATIC_ASSERT( + std::is_same_v, 2>>); + LIBCPP_ASSERT(store.__args.size() == 2); + } + { + auto store = std::make_format_args(1, 'c', nullptr); + LIBCPP_STATIC_ASSERT( + std::is_same_v>); + LIBCPP_STATIC_ASSERT( + std::is_same_v, 3>>); + LIBCPP_ASSERT(store.__args.size() == 3); + } +} + +void test() { + test(); + test(); +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 + +// + +// template +// format-arg-store make_format_args(const Args&... args); + +#include +#include +#include +#include + +#include "test_basic_format_arg.h" +#include "test_macros.h" + +int main(int, char**) { + using Context = std::basic_format_context< + std::back_insert_iterator>, char>; + + auto value = std::make_format_args(42, nullptr, false, 1.0); + + LIBCPP_ASSERT(value.__args.size() == 4); + LIBCPP_ASSERT(test_basic_format_arg(value.__args[0], 42)); + // Note [format.arg]/11 specifies a nullptr is stored as a const void*. + LIBCPP_ASSERT(test_basic_format_arg(value.__args[1], + static_cast(nullptr))); + LIBCPP_ASSERT(test_basic_format_arg(value.__args[2], false)); + LIBCPP_ASSERT(test_basic_format_arg(value.__args[3], 1.0)); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.sh.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.sh.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.sh.cpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// Validate it works regardless of the signedness of `char`. +// RUN: %{cxx} %{flags} %{compile_flags} -fsigned-char -fsyntax-only %s +// RUN: %{cxx} %{flags} %{compile_flags} -funsigned-char -fsyntax-only %s + +// + +// [format.arg]/5.2 +// - otherwise, if T is char and char_type is wchar_t, initializes value with static_cast(v); + +#include + +void test() { + std::make_format_args>, wchar_t>>('c'); +} diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format +// TODO FMT Evaluate gcc-11 status +// UNSUPPORTED: gcc-11 + +// + +// template +// format-arg-store +// make_wformat_args(const Args&... args); + +#include +#include + +#include "test_basic_format_arg.h" +#include "test_macros.h" + +int main(int, char**) { + using Context = std::basic_format_context< + std::back_insert_iterator>, wchar_t>; + + auto value = std::make_wformat_args(42, nullptr, false, 1.0); + + LIBCPP_ASSERT(value.__args.size() == 4); + LIBCPP_ASSERT(test_basic_format_arg(value.__args[0], 42)); + // Note [format.arg]/11 specifies a nullptr is stored as a const void*. + LIBCPP_ASSERT(test_basic_format_arg(value.__args[1], + static_cast(nullptr))); + LIBCPP_ASSERT(test_basic_format_arg(value.__args[2], false)); + LIBCPP_ASSERT(test_basic_format_arg(value.__args[3], 1.0)); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg/ctor.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg/ctor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg/ctor.pass.cpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// + +// basic_format_arg() noexcept; + +// The class has several exposition only private constructors. These are tested +// in visit_format_arg.pass.cpp + +#include +#include + +#include "test_macros.h" + +template +void test() { + using Context = std::basic_format_context; + + ASSERT_NOEXCEPT(std::basic_format_arg{}); + + std::basic_format_arg format_arg{}; + assert(!format_arg); +} + +void test() { + test(); + test(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg/operator_bool.pass.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// + +// explicit operator bool() const noexcept +// +// Note more testing is done in the unit test for: +// template +// see below visit_format_arg(Visitor&& vis, basic_format_arg arg); + +#include +#include +#include + +#include "test_macros.h" + +void test(const auto& store) { + for (const auto& arg : store.__args) { + assert(arg); + assert(static_cast(arg)); + } +} + +template +void test() { + using Context = std::basic_format_context; + { + std::basic_format_arg format_arg{}; + ASSERT_NOEXCEPT(!format_arg); + assert(!format_arg); + ASSERT_NOEXCEPT(static_cast(format_arg)); + assert(!static_cast(format_arg)); + } + test(std::make_format_args()); + test(std::make_format_args(1)); + test(std::make_format_args(1, 'c')); + test(std::make_format_args(1, 'c', nullptr)); +} + +void test() { + test(); + test(); +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp @@ -0,0 +1,360 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// This test requires the dylib support introduced in D92214. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// + +// template +// see below visit_format_arg(Visitor&& vis, basic_format_arg arg); + +#include +#include +#include + +#include "constexpr_char_traits.h" +#include "test_macros.h" +#include "make_string.h" +#include "min_allocator.h" + +template +void test(From value) { + auto format_args = std::make_format_args(value); + assert(format_args.__args.size() == 1); + assert(format_args.__args[0]); + + auto result = std::visit_format_arg( + [v = To(value)](auto a) -> To { + if constexpr (std::is_same_v) { + assert(v == a); + return a; + } else { + assert(false); + return {}; + } + }, + format_args.__args[0]); + + using ct = std::common_type_t; + assert(static_cast(result) == static_cast(value)); +} + +// Test specific for string and string_view. +// +// Since both result in a string_view there's no need to pass this as a +// template argument. +template +void test_string_view(From value) { + auto format_args = std::make_format_args(value); + assert(format_args.__args.size() == 1); + assert(format_args.__args[0]); + + using CharT = typename Context::char_type; + using To = std::basic_string_view; + using V = std::basic_string; + auto result = std::visit_format_arg( + [v = V(value.begin(), value.end())](auto a) -> To { + if constexpr (std::is_same_v) { + assert(v == a); + return a; + } else { + assert(false); + return {}; + } + }, + format_args.__args[0]); + + assert(std::equal(value.begin(), value.end(), result.begin(), result.end())); +} + +template +void test() { + using Context = std::basic_format_context; + std::basic_string empty; + std::basic_string str = MAKE_STRING(CharT, "abc"); + + // Test boolean types. + + test(true); + test(false); + + // Test CharT types. + + test('a'); + test('z'); + test('0'); + test('9'); + + // Test char types. + + if (std::is_same_v) { + // char to char -> char + test('a'); + test('z'); + test('0'); + test('9'); + } else { + if (std::is_same_v) { + // char to wchar_t -> wchar_t + test('a'); + test('z'); + test('0'); + test('9'); + } else if (std::is_signed_v) { + // char to CharT -> int + // This happens when CharT is a char8_t, char16_t, or char32_t and char + // is a signed type. + // Note if sizeof(CharT) > sizeof(int) this test fails. If there are + // platforms where that occurs extra tests need to be added for char32_t + // testing it against a long long. + test('a'); + test('z'); + test('0'); + test('9'); + } else { + // char to CharT -> unsigned + // This happens when CharT is a char8_t, char16_t, or char32_t and char + // is an unsigned type. + // Note if sizeof(CharT) > sizeof(unsigned) this test fails. If there are + // platforms where that occurs extra tests need to be added for char32_t + // testing it against an unsigned long long. + test('a'); + test('z'); + test('0'); + test('9'); + } + } + + // Test signed integer types. + + test(std::numeric_limits::min()); + test(0); + test(std::numeric_limits::max()); + + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(0); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(0); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + + using LongToType = + std::conditional_t; + + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(0); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(0); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + +#ifndef _LIBCPP_HAS_NO_INT128 + test(std::numeric_limits<__int128_t>::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test( + std::numeric_limits::min()); + test(0); + test( + std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits<__int128_t>::max()); +#endif + + // Test unsigned integer types. + + test(0); + test( + std::numeric_limits::max()); + + test(0); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + + test(0); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + + using UnsignedLongToType = + std::conditional_t; + + test(0); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + + test(0); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + +#ifndef _LIBCPP_HAS_NO_INT128 + test(0); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits<__uint128_t>::max()); +#endif + + // Test floating point types. + + test(-std::numeric_limits::max()); + test(-std::numeric_limits::min()); + test(-0.0); + test(0.0); + test(std::numeric_limits::min()); + test(std::numeric_limits::max()); + + test(-std::numeric_limits::max()); + test(-std::numeric_limits::min()); + test(-0.0); + test(0.0); + test(std::numeric_limits::min()); + test(std::numeric_limits::max()); + + test( + -std::numeric_limits::max()); + test( + -std::numeric_limits::min()); + test(-0.0); + test(0.0); + test( + std::numeric_limits::min()); + test( + std::numeric_limits::max()); + + // Test const CharT pointer types. + + test(empty.c_str()); + test(str.c_str()); + + // Test string_view types. + + { + using From = std::basic_string_view; + + test_string_view(From()); + test_string_view(From(empty.c_str())); + test_string_view(From(str.c_str())); + } + + { + using From = std::basic_string_view>; + + test_string_view(From()); + test_string_view(From(empty.c_str())); + test_string_view(From(str.c_str())); + } + + // Test string types. + + { + using From = std::basic_string; + + test_string_view(From()); + test_string_view(From(empty.c_str())); + test_string_view(From(str.c_str())); + } + + { + using From = std::basic_string, + std::allocator>; + + test_string_view(From()); + test_string_view(From(empty.c_str())); + test_string_view(From(str.c_str())); + } + + { + using From = + std::basic_string, min_allocator>; + + test_string_view(From()); + test_string_view(From(empty.c_str())); + test_string_view(From(str.c_str())); + } + + { + using From = std::basic_string, + min_allocator>; + + test_string_view(From()); + test_string_view(From(empty.c_str())); + test_string_view(From(str.c_str())); + } + + // Test pointer types. + + test(nullptr); +} + +void test() { + test(); + test(); +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.arguments/format.args/ctor.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.args/ctor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.arguments/format.args/ctor.pass.cpp @@ -0,0 +1,67 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// + +// basic_format_args() noexcept; +// template +// basic_format_args(const format-arg-store& store) noexcept; + +#include +#include + +#include "test_macros.h" + +template +void test() { + using Context = std::basic_format_context; + { + ASSERT_NOEXCEPT(std::basic_format_args{}); + + std::basic_format_args format_args{}; + assert(!format_args.get(0)); + } + { + auto store = std::make_format_args(1); + ASSERT_NOEXCEPT(std::basic_format_args{store}); + std::basic_format_args format_args{store}; + assert(format_args.get(0)); + assert(!format_args.get(1)); + } + { + auto store = std::make_format_args(1, 'c'); + ASSERT_NOEXCEPT(std::basic_format_args{store}); + std::basic_format_args format_args{store}; + assert(format_args.get(0)); + assert(format_args.get(1)); + assert(!format_args.get(2)); + } + { + auto store = std::make_format_args(1, 'c', nullptr); + ASSERT_NOEXCEPT(std::basic_format_args{store}); + std::basic_format_args format_args{store}; + assert(format_args.get(0)); + assert(format_args.get(1)); + assert(format_args.get(2)); + assert(!format_args.get(3)); + } +} + +void test() { + test(); + test(); +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.arguments/format.args/get.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.args/get.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.arguments/format.args/get.pass.cpp @@ -0,0 +1,314 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// This test requires the dylib support introduced in D92214. +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14|15}} + +// + +// basic_format_arg get(size_t i) const noexcept; + +#include +#include +#include + +#include "test_macros.h" +#include "make_string.h" + +template +void test(From value) { + auto store = std::make_format_args(value); + const std::basic_format_args format_args{store}; + + std::visit_format_arg( + [v = To(value)](auto a) { + if constexpr (std::is_same_v) + assert(v == a); + else + assert(false); + }, + format_args.get(0)); +} + +// Test specific for string and string_view. +// +// Since both result in a string_view there's no need to pass this as a +// template argument. +template +void test_string_view(From value) { + auto store = std::make_format_args(value); + const std::basic_format_args format_args{store}; + + using CharT = typename Context::char_type; + using To = std::basic_string_view; + using V = std::basic_string; + std::visit_format_arg( + [v = V(value.begin(), value.end())](auto a) { + if constexpr (std::is_same_v) + assert(v == a); + else + assert(false); + }, + format_args.get(0)); +} + +template +void test() { + using Context = std::basic_format_context; + { + const std::basic_format_args format_args{}; + ASSERT_NOEXCEPT(format_args.get(0)); + assert(!format_args.get(0)); + } + + using char_type = typename Context::char_type; + std::basic_string empty; + std::basic_string str = MAKE_STRING(char_type, "abc"); + + // Test boolean types. + + test(true); + test(false); + + // Test char_type types. + + test('a'); + test('z'); + test('0'); + test('9'); + + // Test char types. + + if (std::is_same_v) { + // char to char -> char + test('a'); + test('z'); + test('0'); + test('9'); + } else { + if (std::is_same_v) { + // char to wchar_t -> wchar_t + test('a'); + test('z'); + test('0'); + test('9'); + } else if (std::is_signed_v) { + // char to char_type -> int + // This happens when Context::char_type is a char8_t, char16_t, or + // char32_t and char is a signed type. + // Note if sizeof(char_type) > sizeof(int) this test fails. If there are + // platforms where that occurs extra tests need to be added for char32_t + // testing it against a long long. + test('a'); + test('z'); + test('0'); + test('9'); + } else { + // char to char_type -> unsigned + // This happens when Context::char_type is a char8_t, char16_t, or + // char32_t and char is an unsigned type. + // Note if sizeof(char_type) > sizeof(unsigned) this test fails. If there + // are platforms where that occurs extra tests need to be added for + // char32_t testing it against an unsigned long long. + test('a'); + test('z'); + test('0'); + test('9'); + } + } + + // Test signed integer types. + + test(std::numeric_limits::min()); + test(0); + test(std::numeric_limits::max()); + + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(0); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(0); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + + using LongToType = + std::conditional_t; + + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(0); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(0); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + +#ifndef _LIBCPP_HAS_NO_INT128 + test(std::numeric_limits<__int128_t>::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test(std::numeric_limits::min()); + test( + std::numeric_limits::min()); + test(0); + test( + std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits<__int128_t>::max()); +#endif + + // Test unsigned integer types. + + test(0); + test( + std::numeric_limits::max()); + + test(0); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + + test(0); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + test(std::numeric_limits::max()); + + using UnsignedLongToType = + std::conditional_t; + + test(0); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + + test(0); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + +#ifndef _LIBCPP_HAS_NO_INT128 + test(0); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits::max()); + test( + std::numeric_limits<__uint128_t>::max()); +#endif + + // Test floating point types. + + test(-std::numeric_limits::max()); + test(-std::numeric_limits::min()); + test(-0.0); + test(0.0); + test(std::numeric_limits::min()); + test(std::numeric_limits::max()); + + test(-std::numeric_limits::max()); + test(-std::numeric_limits::min()); + test(-0.0); + test(0.0); + test(std::numeric_limits::min()); + test(std::numeric_limits::max()); + + test( + -std::numeric_limits::max()); + test( + -std::numeric_limits::min()); + test(-0.0); + test(0.0); + test( + std::numeric_limits::min()); + test( + std::numeric_limits::max()); + + // Test const char_type pointer types. + + test(empty.c_str()); + test(str.c_str()); + + // Test string_view types. + + test>( + std::basic_string_view()); + test, + std::basic_string_view>(empty); + test, + std::basic_string_view>(str); + + // Test string types. + + test>( + std::basic_string()); + test, + std::basic_string>(empty); + test, + std::basic_string>(str); + + // Test pointer types. + + test(nullptr); +} + +void test() { + test(); + test(); +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.arguments/format.args/types.compile.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.args/types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.arguments/format.args/types.compile.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// + +// Namespace std typedefs: +// using format_args = basic_format_args; +// using wformat_args = basic_format_args; +// template +// using format_args_t = basic_format_args>; + +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::is_same_v>); +static_assert(std::is_same_v>); + +static_assert(std::is_same_v< + std::format_args_t, char>, + std::basic_format_args, char>>>); + +static_assert( + std::is_same_v< + std::format_args_t, wchar_t>, + std::basic_format_args, wchar_t>>>); + +static_assert( + std::is_same_v< + std::format_args_t>, char>, + std::basic_format_args>, char>>>); + +// Required for MSVC internal test runner compatibility. +int main(int, char**) { return 0; } diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/advance_to.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/advance_to.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/advance_to.pass.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// + +// void advance_to(iterator it); + +#include +#include +#include + +#include "test_macros.h" +#include "test_format_context.h" + +template +void test( + std::basic_format_args> args) { + { + std::basic_string str[3]; + std::basic_format_context context = + test_format_context_create(OutIt{str[0]}, args); + context.out() = CharT('a'); + context.advance_to(OutIt{str[1]}); + context.out() = CharT('b'); + context.advance_to(OutIt{str[2]}); + context.out() = CharT('c'); + + assert(str[0].size() == 1); + assert(str[0].front() == CharT('a')); + assert(str[1].size() == 1); + assert(str[1].front() == CharT('b')); + assert(str[2].size() == 1); + assert(str[2].front() == CharT('c')); + } +} + +void test() { + test(std::basic_format_args( + std::make_format_args>, char>>())); + + test(std::basic_format_args( + std::make_format_args>, wchar_t>>())); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(std::basic_format_args( + std::make_format_args>, char8_t>>())); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(std::basic_format_args( + std::make_format_args>, + char16_t>>())); + test(std::basic_format_args( + std::make_format_args>, + char32_t>>())); +#endif +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// + +// basic_format_arg arg(size_t id) const; + +#include +#include + +#include "test_basic_format_arg.h" +#include "test_format_context.h" +#include "test_macros.h" +#include "make_string.h" + +template +void test() { + std::basic_string string = MAKE_STRING(CharT, "string"); + auto store = std::make_format_args>( + true, CharT('a'), 42, string); + std::basic_format_args args = store; + + std::basic_string output; + const std::basic_format_context context = + test_format_context_create(OutIt{output}, args); + LIBCPP_ASSERT(args.__size() == 4); + for (size_t i = 0, e = args.__size(); i != e; ++i) { + assert(context.arg(i)); + } + assert(!context.arg(args.__size())); + + assert(test_basic_format_arg(context.arg(0), true)); + assert(test_basic_format_arg(context.arg(1), CharT('a'))); + assert(test_basic_format_arg(context.arg(2), 42)); + assert(test_basic_format_arg(context.arg(3), + std::basic_string_view(string))); +} + +int main(int, char**) { + test>, char>(); + test>, wchar_t>(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test>, char8_t>(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test>, char16_t>(); + test>, char32_t>(); +#endif + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp @@ -0,0 +1,139 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-localization +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// REQUIRES: locale.en_US.UTF-8 +// REQUIRES: locale.fr_FR.UTF-8 + +// + +// The Standard does not specifiy a constructor +// basic_format_context(Out out, +// basic_format_args args, +// std::optional&& loc = std::nullopt); +// If compliled with -D_LIBCPP_HAS_NO_LOCALIZATION +// basic_format_context(Out out, +// basic_format_args args); + +#include +#include +#include + +#include "test_basic_format_arg.h" +#include "test_format_context.h" +#include "make_string.h" +#include "platform_support.h" // locale name macros +#include "test_macros.h" + +template +void test() { + static_assert( + !std::is_copy_constructible_v>); + static_assert( + !std::is_copy_assignable_v>); + // The move operations are implicitly deleted due to the + // deleted copy operations. + static_assert( + !std::is_move_constructible_v>); + static_assert( + !std::is_move_assignable_v>); + + std::basic_string string = MAKE_STRING(CharT, "string"); + std::basic_format_args args = + std::make_format_args>( + true, CharT('a'), 42, string); + + { + std::basic_string output; + OutIt out_it{output}; + std::basic_format_context context = + test_format_context_create(out_it, args); + LIBCPP_ASSERT(args.__size() == 4); + + assert(test_basic_format_arg(context.arg(0), true)); + assert(test_basic_format_arg(context.arg(1), CharT('a'))); + assert(test_basic_format_arg(context.arg(2), 42)); + assert(test_basic_format_arg(context.arg(3), + std::basic_string_view(string))); + + context.out() = CharT('a'); + assert(output.size() == 1); + assert(output.front() == CharT('a')); + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + assert(context.locale() == std::locale()); +#endif + } + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION + std::locale en_US{LOCALE_en_US_UTF_8}; + std::locale fr_FR{LOCALE_fr_FR_UTF_8}; + { + std::basic_string output; + OutIt out_it{output}; + std::basic_format_context context = + test_format_context_create(out_it, args, en_US); + + LIBCPP_ASSERT(args.__size() == 4); + assert(test_basic_format_arg(context.arg(0), true)); + assert(test_basic_format_arg(context.arg(1), CharT('a'))); + assert(test_basic_format_arg(context.arg(2), 42)); + assert(test_basic_format_arg(context.arg(3), + std::basic_string_view(string))); + + context.out() = CharT('a'); + assert(output.size() == 1); + assert(output.front() == CharT('a')); + + assert(context.locale() != fr_FR); + assert(context.locale() == en_US); + } + + { + std::basic_string output; + OutIt out_it{output}; + std::basic_format_context context = + test_format_context_create(out_it, args, fr_FR); + + LIBCPP_ASSERT(args.__size() == 4); + assert(test_basic_format_arg(context.arg(0), true)); + assert(test_basic_format_arg(context.arg(1), CharT('a'))); + assert(test_basic_format_arg(context.arg(2), 42)); + assert(test_basic_format_arg(context.arg(3), + std::basic_string_view(string))); + + context.out() = CharT('a'); + assert(output.size() == 1); + assert(output.front() == CharT('a')); + + assert(context.locale() == fr_FR); + assert(context.locale() != en_US); + } +#endif +} + +void test() { + test>, char>(); + test>, wchar_t>(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test>, char8_t>(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test>, char16_t>(); + test>, char32_t>(); +#endif +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp @@ -0,0 +1,94 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-localization +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// REQUIRES: locale.en_US.UTF-8 +// REQUIRES: locale.fr_FR.UTF-8 + +// + +// std::locale locale(); + +#include +#include + +#include "make_string.h" +#include "platform_support.h" // locale name macros +#include "test_basic_format_arg.h" +#include "test_format_context.h" +#include "test_macros.h" + +template +void test() { + std::locale en_US{LOCALE_en_US_UTF_8}; + std::locale fr_FR{LOCALE_fr_FR_UTF_8}; + std::basic_string string = MAKE_STRING(CharT, "string"); + std::basic_format_args args = + std::make_format_args>( + true, CharT('a'), 42, string); + + { + std::basic_string output; + OutIt out_it{output}; + std::basic_format_context context = + test_format_context_create(out_it, args, en_US); + assert(args.__size() == 4); + assert(test_basic_format_arg(context.arg(0), true)); + assert(test_basic_format_arg(context.arg(1), CharT('a'))); + assert(test_basic_format_arg(context.arg(2), 42)); + assert(test_basic_format_arg(context.arg(3), + std::basic_string_view(string))); + + context.out() = CharT('a'); + assert(output.size() == 1); + assert(output.front() == CharT('a')); + + assert(context.locale() != fr_FR); + assert(context.locale() == en_US); + } + + { + std::basic_string output; + OutIt out_it{output}; + std::basic_format_context context = + test_format_context_create(out_it, args, fr_FR); + assert(args.__size() == 4); + assert(test_basic_format_arg(context.arg(0), true)); + assert(test_basic_format_arg(context.arg(1), CharT('a'))); + assert(test_basic_format_arg(context.arg(2), 42)); + assert(test_basic_format_arg(context.arg(3), + std::basic_string_view(string))); + + context.out() = CharT('a'); + assert(output.size() == 1); + assert(output.front() == CharT('a')); + + assert(context.locale() == fr_FR); + assert(context.locale() != en_US); + } +} + +void test() { + test>, char>(); + test>, wchar_t>(); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test>, char8_t>(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test>, char16_t>(); + test>, char32_t>(); +#endif +} +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/out.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/out.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/out.pass.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// + +// iterator out(); + +#include +#include +#include + +#include "test_macros.h" +#include "test_format_context.h" + +template +void test( + std::basic_format_args> args) { + { + std::basic_string str; + OutIt out_it{str}; + std::basic_format_context context = + test_format_context_create(out_it, args); + context.out() = CharT('a'); + context.out() = CharT('b'); + context.out() = CharT('c'); + + assert(str.size() == 3); + assert(str[0] == CharT('a')); + assert(str[1] == CharT('b')); + assert(str[2] == CharT('c')); + } +} + +void test() { + test(std::basic_format_args( + std::make_format_args>, char>>())); + test(std::basic_format_args( + std::make_format_args>, wchar_t>>())); +#ifndef _LIBCPP_HAS_NO_CHAR8_T + test(std::basic_format_args( + std::make_format_args>, char8_t>>())); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(std::basic_format_args( + std::make_format_args>, + char16_t>>())); + test(std::basic_format_args( + std::make_format_args>, + char32_t>>())); +#endif +} + +int main(int, char**) { + test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/types.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/types.compile.pass.cpp @@ -0,0 +1,119 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// + +// Class typedefs: +// template +// class basic_format_context { +// public: +// using iterator = Out +// using char_type = charT; +// template using formatter_type = formatter; +// } +// +// Namespace std typedefs: +// using format_context = basic_format_context; +// using wformat_context = basic_format_context; + +#include +#include +#include +#include + +#include "test_macros.h" + +template +constexpr void test() { + static_assert( + std::is_same_v::iterator, + OutIt>); + static_assert( + std::is_same_v< + typename std::basic_format_context::char_type, CharT>); + static_assert(std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); + static_assert(std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v:: + template formatter_type, + std::formatter>); +#ifndef _LIBCPP_HAS_NO_INT128 + static_assert( + std::is_same_v::template formatter_type<__int128_t>, + std::formatter<__int128_t, CharT>>); + static_assert( + std::is_same_v::template formatter_type<__uint128_t>, + std::formatter<__uint128_t, CharT>>); +#endif + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); + static_assert( + std::is_same_v:: + template formatter_type>, + std::formatter, CharT>>); + static_assert( + std::is_same_v::template formatter_type, + std::formatter>); +} + +constexpr void test() { + test>, char>(); + test>, wchar_t>(); + test>, char8_t>(); + test>, char16_t>(); + test>, char32_t>(); +} + +static_assert(std::is_same_v< + std::format_context, + std::basic_format_context< + std::back_insert_iterator>, char>>); +static_assert( + std::is_same_v< + std::wformat_context, + std::basic_format_context< + std::back_insert_iterator>, wchar_t>>); + +// Required for MSVC internal test runner compatibility. +int main(int, char**) { return 0; } diff --git a/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/check_arg_id.verify.cpp b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/check_arg_id.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/check_arg_id.verify.cpp @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-format + +// constexpr void check_arg_id(size_t id); + +#include + +#include "test_macros.h" + +constexpr bool test() { + // [format.parse.ctx]/11 + // Remarks: Call expressions where id >= num_args_ are not + // core constant expressions ([expr.const]). + std::format_parse_context context("", 0); + context.check_arg_id(1); + + return true; +} + +int main(int, char**) { + // expected-error@+1 {{static_assert expression is not an integral constant expression}} + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/support/test_basic_format_arg.h b/libcxx/test/support/test_basic_format_arg.h new file mode 100644 --- /dev/null +++ b/libcxx/test/support/test_basic_format_arg.h @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include +#include + +#include "test_macros.h" + +/// Returns whether the basic_format_arg contains a type T with the expected value. +template +bool test_basic_format_arg(std::basic_format_arg arg, T expected) { + return std::visit_format_arg( + [expected](auto a) { + if constexpr (std::same_as) + return a == expected; + else + return false; + }, + arg); +} diff --git a/libcxx/test/support/test_format_context.h b/libcxx/test/support/test_format_context.h new file mode 100644 --- /dev/null +++ b/libcxx/test/support/test_format_context.h @@ -0,0 +1,62 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef SUPPORT_TEST_FORMAT_CONTEXT_HPP +#define SUPPORT_TEST_FORMAT_CONTEXT_HPP + +/** + * @file Helper functions to create a @ref std::basic_format_context. + * + * Since the standard doesn't specify how a @ref std::basic_format_context is + * constructed this is implementation defined. To make the public API tests of + * the class generic this header defines helper functions to create the + * required object. + * + * @note This requires every standard library implementation to write their own + * helper function. Vendors are encouraged to file a review at + * https://reviews.llvm.org/ so their specific implementation can be part of + * this file. + */ + +#if TEST_STD_VER < 20 +#error "The format header requires at least C++20" +#endif + +#include + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +#include +#endif + +#if defined(_LIBCPP_VERSION) + +/** Creates a std::basic_format_context as-if the formatting function takes no locale. */ +template +std::basic_format_context test_format_context_create( + OutIt out_it, + std::basic_format_args> args) { + return std::__format_context_create(std::move(out_it), args); +} + +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +/** Creates a std::basic_format_context as-if the formatting function takes locale. */ +template +std::basic_format_context test_format_context_create( + OutIt out_it, + std::basic_format_args> args, + std::locale loc) { + return std::__format_context_create(std::move(out_it), args, std::move(loc)); +} +#endif +#else +#error \ + "Please create a vendor specific version of the test functions and file a review at https://reviews.llvm.org/" +#endif + +#endif // SUPPORT_TEST_FORMAT_CONTEXT_HPP