diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -310,6 +310,7 @@ __format/formatter_pointer.h __format/formatter_string.h __format/parser_std_format_spec.h + __format/range_default_formatter.h __format/unicode.h __functional/binary_function.h __functional/binary_negate.h @@ -625,6 +626,7 @@ __type_traits/is_scoped_enum.h __type_traits/is_signed.h __type_traits/is_signed_integer.h + __type_traits/is_specialization.h __type_traits/is_standard_layout.h __type_traits/is_swappable.h __type_traits/is_trivial.h diff --git a/libcxx/include/__format/concepts.h b/libcxx/include/__format/concepts.h --- a/libcxx/include/__format/concepts.h +++ b/libcxx/include/__format/concepts.h @@ -15,6 +15,9 @@ #include <__config> #include <__format/format_fwd.h> #include <__format/format_parse_context.h> +#include <__type_traits/is_specialization.h> +#include <__utility/pair.h> +#include #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -56,6 +59,16 @@ # if _LIBCPP_STD_VER > 20 template concept formattable = __formattable<_Tp, _CharT>; + +// The paper P2165 "Compatibility between tuple, pair and tuple-like objects" +// has a "tuple-like" concept, but that is not the same as this concept. That +// paper doesn't affect the format header. Therefore use a different name for +// the concept. +template +concept __fmt_tuple_like = __is_specialization_v<_Tp, pair> || + // Use a requires since tuple_size_v may fail to instantiate, + (__is_specialization_v<_Tp, tuple> && requires { tuple_size_v<_Tp> == 2; }); + # endif //_LIBCPP_STD_VER > 20 #endif //_LIBCPP_STD_VER > 17 diff --git a/libcxx/include/__format/range_default_formatter.h b/libcxx/include/__format/range_default_formatter.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__format/range_default_formatter.h @@ -0,0 +1,130 @@ +// -*- 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_RANGE_DEFAULT_FORMATTER_H +#define _LIBCPP___FORMAT_RANGE_DEFAULT_FORMATTER_H + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#include <__availability> +#include <__concepts/same_as.h> +#include <__config> +#include <__format/concepts.h> +#include <__format/formatter.h> +#include <__ranges/concepts.h> +#include <__type_traits/remove_cvref.h> +#include <__utility/pair.h> +#include + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 20 + +template +concept __const_formattable_range = + ranges::input_range && formattable, _CharT>; + +template +using __fmt_maybe_const = conditional_t<__const_formattable_range<_Rp, _CharT>, const _Rp, _Rp>; + +enum class range_format { disabled, map, set, sequence, string, debug_string }; + +// There is no definition of this struct, it's purely intended to be used to +// generate diagnostics. +template +struct _LIBCPP_TEMPLATE_VIS __instantiated_the_primary_template_of_format_kind; + +// Instantiation of the primary template is ill-formed. +// This fails since the templated struct can't be instantiated. When the type +// of format_kind is the templated struct it will generate two diagnostics per +// instantiation. By using int as a type there's only one diagnostic. +template +constexpr int format_kind = __instantiated_the_primary_template_of_format_kind<_Rp>{}; + +template +consteval range_format __get_format_format() { + // [format.range.fmtkind]/2 + + // 2.1 If same_as>, R> is true, + // Otherwise format_kind is range_format::disabled. + if constexpr (same_as>, _Rp>) + return range_format::disabled; + // 2.2 Otherwise, if the qualified-id R::key_type is valid and denotes a type: + else if constexpr (requires { typename _Rp::key_type; }) { + // 2.2.1 If the qualified-id R::mapped_type is valid and denotes a type ... + if constexpr (requires { typename _Rp::mapped_type; } && + // 2.2.1 ... If either U is a specialization of pair or U is a specialization + // of tuple and tuple_size_v == 2 + __fmt_tuple_like>>) + return range_format::map; + else + // 2.2.2 Otherwise format_kind is range_format::set. + return range_format::set; + } else + // 2.3 Otherwise, format_kind is range_format::sequence. + return range_format::sequence; +} + +template + requires same_as<_Rp, remove_cvref_t<_Rp>> +constexpr range_format format_kind<_Rp> = __get_format_format<_Rp>(); + +// This is a non-standard work-around to fix instantiation of +// formatter +// const _CharT[N] satisfies the ranges::input_range concept. +// remove_cvref_t is _CharT[N] so it does not satisfy the +// requirement of the above specialization. Instead it will instantiate the +// primary template, which is ill-formed. +// +// An alternative solution is to remove the offending formatter. +// +// https://godbolt.org/z/bqjhhaexx +template +constexpr range_format format_kind = range_format::disabled; + +template +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter; + +// Required specializations + +template +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter { + __range_default_formatter() = delete; // TODO FMT Implement +}; + +template +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter { + __range_default_formatter() = delete; // TODO FMT Implement +}; + +template +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter { + __range_default_formatter() = delete; // TODO FMT Implement +}; + +template + requires(_Kp == range_format::string || _Kp == range_format::debug_string) +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter { + __range_default_formatter() = delete; // TODO FMT Implement +}; + +// Dispatcher to select the specialization based on the type of the range. + +template + requires(format_kind<_Rp> != range_format::disabled && formattable, _CharT>) +struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<_Rp, _CharT> + : __range_default_formatter, _Rp, _CharT> {}; + +#endif //_LIBCPP_STD_VER > 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___FORMAT_RANGE_DEFAULT_FORMATTER_H diff --git a/libcxx/include/__type_traits/is_specialization.h b/libcxx/include/__type_traits/is_specialization.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__type_traits/is_specialization.h @@ -0,0 +1,45 @@ +// -*- 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___TYPE_TRAITS_IS_SPECIALIZATION +#define _LIBCPP___TYPE_TRAITS_IS_SPECIALIZATION + +// This contains parts of P2098R1 but is based on MSVC STL's implementation. +// +// The paper has been rejected +// We will not pursue P2098R0 (std::is_specialization_of) at this time; we'd +// like to see a solution to this problem, but it requires language evolution +// too. +// +// Since it is expected a real solution will be provided in the future only the +// minimal part is implemented. +// +// Note a cvref qualified _Tp is never considered a specialization. + +#include <__config> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 14 + +template class _Template> +inline constexpr bool __is_specialization_v = false; // true if and only if _Tp is a specialization of _Template + +template