[libcxx-commits] [libcxx] d184958 - [libc++][format] Adds range-default-formatter.

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Wed Dec 7 08:33:00 PST 2022


Author: Mark de Wever
Date: 2022-12-07T17:32:55+01:00
New Revision: d184958bad5c23e11ee91a16d9c551e1922387bf

URL: https://github.com/llvm/llvm-project/commit/d184958bad5c23e11ee91a16d9c551e1922387bf
DIFF: https://github.com/llvm/llvm-project/commit/d184958bad5c23e11ee91a16d9c551e1922387bf.diff

LOG: [libc++][format] Adds range-default-formatter.

This adds an incomplete version where the specializations for the
format_kinds are disabled dummy formatters.

Implements part of
- P2585R0 Improving default container formatting

Reviewed By: ldionne, #libc

Differential Revision: https://reviews.llvm.org/D137271

Added: 
    libcxx/include/__format/range_default_formatter.h
    libcxx/include/__type_traits/is_specialization.h
    libcxx/test/libcxx/type_traits/is_specialization.compile.pass.cpp
    libcxx/test/libcxx/type_traits/is_specialization.verify.cpp
    libcxx/test/std/utilities/format/format.range/format.range.fmtkind/format_kind.compile.pass.cpp
    libcxx/test/std/utilities/format/format.range/format.range.fmtkind/format_kind.verify.cpp
    libcxx/test/std/utilities/format/format.range/format.range.fmtkind/range_format.compile.pass.cpp

Modified: 
    libcxx/include/CMakeLists.txt
    libcxx/include/__format/concepts.h
    libcxx/include/format
    libcxx/include/module.modulemap.in
    libcxx/include/type_traits
    libcxx/test/libcxx/private_headers.verify.cpp
    libcxx/test/libcxx/transitive_includes/cxx03.csv
    libcxx/test/libcxx/transitive_includes/cxx11.csv
    libcxx/test/libcxx/transitive_includes/cxx14.csv
    libcxx/test/libcxx/transitive_includes/cxx17.csv
    libcxx/test/libcxx/transitive_includes/cxx20.csv
    libcxx/test/libcxx/transitive_includes/cxx2b.csv
    libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 1ae1441191678..7dc3ffa252a8a 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -310,6 +310,7 @@ set(files
   __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
@@ -626,6 +627,7 @@ set(files
   __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
index 5407feebf77bd..fe4a7b9625ce3 100644
--- 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 <tuple>
 #include <type_traits>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -56,6 +59,17 @@ concept __formattable =
 #  if _LIBCPP_STD_VER > 20
 template <class _Tp, class _CharT>
 concept formattable = __formattable<_Tp, _CharT>;
+
+// [tuple.like] defines a tuple-like exposition only concept. This concept is
+// not related to that. Therefore it uses a 
diff erent name for the concept.
+//
+// TODO FMT Add a test to validate we fail when using that concept after P2165
+// has been implemented.
+template <class _Tp>
+concept __fmt_pair_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
index 0000000000000..56558f3ffb6c4
--- /dev/null
+++ b/libcxx/include/__format/range_default_formatter.h
@@ -0,0 +1,137 @@
+// -*- 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 <tuple>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 20
+
+template <class _Rp, class _CharT>
+concept __const_formattable_range =
+    ranges::input_range<const _Rp> && formattable<ranges::range_reference_t<const _Rp>, _CharT>;
+
+template <class _Rp, class _CharT>
+using __fmt_maybe_const = conditional_t<__const_formattable_range<_Rp, _CharT>, const _Rp, _Rp>;
+
+_LIBCPP_DIAGNOSTIC_PUSH
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wshadow")
+_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wshadow")
+// This shadows map, set, and string.
+enum class range_format { disabled, map, set, sequence, string, debug_string };
+_LIBCPP_DIAGNOSTIC_POP
+
+// There is no definition of this struct, it's purely intended to be used to
+// generate diagnostics.
+template <class _Rp>
+struct _LIBCPP_TEMPLATE_VIS __instantiated_the_primary_template_of_format_kind;
+
+template <class _Rp>
+constexpr range_format format_kind = [] {
+  // [format.range.fmtkind]/1
+  // A program that instantiates the primary template of format_kind is ill-formed.
+  static_assert(sizeof(_Rp) != sizeof(_Rp), "create a template specialization of format_kind for your type");
+  return range_format::disabled;
+}();
+
+template <ranges::input_range _Rp>
+  requires same_as<_Rp, remove_cvref_t<_Rp>>
+inline constexpr range_format format_kind<_Rp> = [] {
+  // [format.range.fmtkind]/2
+
+  // 2.1 If same_as<remove_cvref_t<ranges::range_reference_t<R>>, R> is true,
+  // Otherwise format_kind<R> is range_format::disabled.
+  if constexpr (same_as<remove_cvref_t<ranges::range_reference_t<_Rp>>, _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<U> == 2
+                  __fmt_pair_like<remove_cvref_t<ranges::range_reference_t<_Rp>>>)
+      return range_format::map;
+    else
+      // 2.2.2 Otherwise format_kind<R> is range_format::set.
+      return range_format::set;
+  } else
+    // 2.3 Otherwise, format_kind<R> is range_format::sequence.
+    return range_format::sequence;
+}();
+
+// This is a non-standard work-around to fix instantiation of
+//   formatter<const _CharT[N], _CharT>
+// const _CharT[N] satisfies the ranges::input_range concept.
+// remove_cvref_t<const _CharT[N]> 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
+//
+// The removal is proposed in LWG3833, but use the work-around until the issue
+// has been adopted.
+// TODO FMT Implement LWG3833.
+template <class _CharT, size_t N>
+inline constexpr range_format format_kind<const _CharT[N]> = range_format::disabled;
+
+template <range_format _Kp, ranges::input_range _Rp, class _CharT>
+struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter;
+
+// Required specializations
+
+template <ranges::input_range _Rp, class _CharT>
+struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter<range_format::sequence, _Rp, _CharT> {
+  __range_default_formatter() = delete; // TODO FMT Implement
+};
+
+template <ranges::input_range _Rp, class _CharT>
+struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter<range_format::map, _Rp, _CharT> {
+  __range_default_formatter() = delete; // TODO FMT Implement
+};
+
+template <ranges::input_range _Rp, class _CharT>
+struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter<range_format::set, _Rp, _CharT> {
+  __range_default_formatter() = delete; // TODO FMT Implement
+};
+
+template <range_format _Kp, ranges::input_range _Rp, class _CharT>
+  requires(_Kp == range_format::string || _Kp == range_format::debug_string)
+struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT __range_default_formatter<_Kp, _Rp, _CharT> {
+  __range_default_formatter() = delete; // TODO FMT Implement
+};
+
+// Dispatcher to select the specialization based on the type of the range.
+
+template <ranges::input_range _Rp, class _CharT>
+  requires(format_kind<_Rp> != range_format::disabled && formattable<ranges::range_reference_t<_Rp>, _CharT>)
+struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<_Rp, _CharT>
+    : __range_default_formatter<format_kind<_Rp>, _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
index 0000000000000..0cc36674257c6
--- /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 _Tp, template <class...> class _Template>
+inline constexpr bool __is_specialization_v = false; // true if and only if _Tp is a specialization of _Template
+
+template <template <class...> class _Template, class... _Args>
+inline constexpr bool __is_specialization_v<_Template<_Args...>, _Template> = true;
+
+#endif // _LIBCPP_STD_VER > 14
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___TYPE_TRAITS_IS_SPECIALIZATION

diff  --git a/libcxx/include/format b/libcxx/include/format
index 8817dceb1e536..e5f897699350e 100644
--- a/libcxx/include/format
+++ b/libcxx/include/format
@@ -112,6 +112,35 @@ namespace std {
   using format_parse_context = basic_format_parse_context<char>;
   using wformat_parse_context = basic_format_parse_context<wchar_t>;
 
+  // [format.range], formatting of ranges
+  // [format.range.fmtkind], variable template format_kind
+  enum class range_format {                                     // since C++23
+    disabled,
+    map,
+    set,
+    sequence,
+    string,
+    debug_string
+  };
+
+  template<class R>
+    constexpr unspecified format_kind = unspecified;            // since C++23
+
+  template<ranges::input_range R>
+      requires same_as<R, remove_cvref_t<R>>
+    constexpr range_format format_kind<R> = see below;          // since C++23
+
+  // [format.range.fmtdef], class template range-default-formatter
+  template<range_format K, ranges::input_range R, class charT>
+    struct range-default-formatter;                             // exposition only, since C++23
+
+  // [format.range.fmtmap], [format.range.fmtset], [format.range.fmtstr],
+  // specializations for maps, sets, and strings
+  template<ranges::input_range R, class charT>
+    requires (format_kind<R> != range_format::disabled) &&
+             formattable<ranges::range_reference_t<R>, charT>
+  struct formatter<R, charT> : range-default-formatter<format_kind<R>, R, charT> { }; // since C++23
+
   // [format.arguments], arguments
   // [format.arg], class template basic_format_arg
   template<class Context> class basic_format_arg;
@@ -163,6 +192,7 @@ namespace std {
 #include <__format/formatter_pointer.h>
 #include <__format/formatter_string.h>
 #include <__format/parser_std_format_spec.h>
+#include <__format/range_default_formatter.h>
 #include <__format/unicode.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 1a33ec2f83ac2..4033d2fe412d4 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -846,6 +846,7 @@ module std [system] {
       module formatter_pointer               { private header "__format/formatter_pointer.h" }
       module formatter_string                { private header "__format/formatter_string.h" }
       module parser_std_format_spec          { private header "__format/parser_std_format_spec.h" }
+      module range_default_formatter         { private header "__format/range_default_formatter.h" }
       module unicode                         { private header "__format/unicode.h" }
     }
   }
@@ -1429,6 +1430,7 @@ module std [system] {
     module is_scoped_enum                      { private header "__type_traits/is_scoped_enum.h" }
     module is_signed                           { private header "__type_traits/is_signed.h" }
     module is_signed_integer                   { private header "__type_traits/is_signed_integer.h" }
+    module is_specialization                   { private header "__type_traits/is_specialization.h" }
     module is_standard_layout                  { private header "__type_traits/is_standard_layout.h" }
     module is_swappable                        { private header "__type_traits/is_swappable.h" }
     module is_trivial                          { private header "__type_traits/is_trivial.h" }

diff  --git a/libcxx/include/type_traits b/libcxx/include/type_traits
index 7587211adaf01..757226f885cc1 100644
--- a/libcxx/include/type_traits
+++ b/libcxx/include/type_traits
@@ -497,6 +497,7 @@ namespace std
 #include <__type_traits/is_scalar.h>
 #include <__type_traits/is_scoped_enum.h>
 #include <__type_traits/is_signed.h>
+#include <__type_traits/is_specialization.h>
 #include <__type_traits/is_standard_layout.h>
 #include <__type_traits/is_swappable.h>
 #include <__type_traits/is_trivial.h>

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 9b1120efd5234..075b5dde3b3c9 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -342,6 +342,7 @@ END-SCRIPT
 #include <__format/formatter_pointer.h> // expected-error@*:* {{use of private header from outside its module: '__format/formatter_pointer.h'}}
 #include <__format/formatter_string.h> // expected-error@*:* {{use of private header from outside its module: '__format/formatter_string.h'}}
 #include <__format/parser_std_format_spec.h> // expected-error@*:* {{use of private header from outside its module: '__format/parser_std_format_spec.h'}}
+#include <__format/range_default_formatter.h> // expected-error@*:* {{use of private header from outside its module: '__format/range_default_formatter.h'}}
 #include <__format/unicode.h> // expected-error@*:* {{use of private header from outside its module: '__format/unicode.h'}}
 #include <__functional/binary_function.h> // expected-error@*:* {{use of private header from outside its module: '__functional/binary_function.h'}}
 #include <__functional/binary_negate.h> // expected-error@*:* {{use of private header from outside its module: '__functional/binary_negate.h'}}
@@ -639,6 +640,7 @@ END-SCRIPT
 #include <__type_traits/is_scoped_enum.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_scoped_enum.h'}}
 #include <__type_traits/is_signed.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_signed.h'}}
 #include <__type_traits/is_signed_integer.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_signed_integer.h'}}
+#include <__type_traits/is_specialization.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_specialization.h'}}
 #include <__type_traits/is_standard_layout.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_standard_layout.h'}}
 #include <__type_traits/is_swappable.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_swappable.h'}}
 #include <__type_traits/is_trivial.h> // expected-error@*:* {{use of private header from outside its module: '__type_traits/is_trivial.h'}}

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index a5f334a04f7a7..e6dcd70c25087 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -347,6 +347,7 @@ format optional
 format stdexcept
 format string
 format string_view
+format tuple
 format type_traits
 format version
 forward_list algorithm

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 6ad398a3b092c..34c2f0f0e8877 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -347,6 +347,7 @@ format optional
 format stdexcept
 format string
 format string_view
+format tuple
 format type_traits
 format version
 forward_list algorithm

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 2b13557fd2460..3f4f7879faa59 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -349,6 +349,7 @@ format optional
 format stdexcept
 format string
 format string_view
+format tuple
 format type_traits
 format version
 forward_list algorithm

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 2b13557fd2460..3f4f7879faa59 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -349,6 +349,7 @@ format optional
 format stdexcept
 format string
 format string_view
+format tuple
 format type_traits
 format version
 forward_list algorithm

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 9f3d88d78c5ae..6462223d630e8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -123,6 +123,7 @@ chrono sstream
 chrono stdexcept
 chrono string
 chrono string_view
+chrono tuple
 chrono type_traits
 chrono version
 cinttypes cstdint
@@ -360,6 +361,7 @@ format optional
 format stdexcept
 format string
 format string_view
+format tuple
 format type_traits
 format version
 forward_list algorithm

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx2b.csv b/libcxx/test/libcxx/transitive_includes/cxx2b.csv
index c8750c2703f2d..18803bc804d3b 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx2b.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx2b.csv
@@ -96,6 +96,7 @@ chrono sstream
 chrono stdexcept
 chrono string
 chrono string_view
+chrono tuple
 chrono type_traits
 chrono version
 cinttypes cstdint
@@ -277,6 +278,7 @@ format optional
 format stdexcept
 format string
 format string_view
+format tuple
 format type_traits
 format version
 forward_list compare

diff  --git a/libcxx/test/libcxx/type_traits/is_specialization.compile.pass.cpp b/libcxx/test/libcxx/type_traits/is_specialization.compile.pass.cpp
new file mode 100644
index 0000000000000..134e5f5d186a6
--- /dev/null
+++ b/libcxx/test/libcxx/type_traits/is_specialization.compile.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
+
+// template <class _Tp, template <class...> class _Template>
+// inline constexpr bool __is_specialization_v = true if and only if _Tp is a specialization of _Template
+//
+// Note instantiation for certain type combinations are ill-formed. These are
+// tested in is_specialization.verify.cpp.
+
+#include <type_traits>
+
+#include <array>
+#include <concepts>
+#include <string_view>
+#include <tuple>
+#include <utility>
+
+#include "test_macros.h"
+
+// Simple types
+static_assert(std::__is_specialization_v<std::pair<int, int>, std::pair>);
+static_assert(!std::__is_specialization_v<std::pair<int, int>, std::tuple>);
+static_assert(!std::__is_specialization_v<std::pair<int, int>, std::basic_string_view>);
+
+static_assert(std::__is_specialization_v<std::tuple<int>, std::tuple>);
+static_assert(std::__is_specialization_v<std::tuple<int, float>, std::tuple>);
+static_assert(std::__is_specialization_v<std::tuple<int, float, void*>, std::tuple>);
+
+static_assert(std::__is_specialization_v<std::string_view, std::basic_string_view>);
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(std::__is_specialization_v<std::wstring_view, std::basic_string_view>);
+#endif
+
+// Nested types
+static_assert(std::__is_specialization_v<std::pair<std::tuple<int>, int>, std::pair>);
+static_assert(!std::__is_specialization_v<std::pair<std::tuple<int>, int>, std::tuple>);
+
+// cvref _Tp is not a specialization.
+static_assert(!std::__is_specialization_v<const std::pair<int, int>, std::pair>);
+static_assert(!std::__is_specialization_v<volatile std::pair<int, int>, std::pair>);
+static_assert(!std::__is_specialization_v<const volatile std::pair<int, int>, std::pair>);
+
+static_assert(!std::__is_specialization_v<std::pair<int, int>&, std::pair>);
+static_assert(!std::__is_specialization_v<const std::pair<int, int>&, std::pair>);
+static_assert(!std::__is_specialization_v<volatile std::pair<int, int>&, std::pair>);
+static_assert(!std::__is_specialization_v<const volatile std::pair<int, int>&, std::pair>);
+
+static_assert(!std::__is_specialization_v<std::pair<int, int>&&, std::pair>);
+static_assert(!std::__is_specialization_v<const std::pair<int, int>&&, std::pair>);
+static_assert(!std::__is_specialization_v<volatile std::pair<int, int>&&, std::pair>);
+static_assert(!std::__is_specialization_v<const volatile std::pair<int, int>&&, std::pair>);

diff  --git a/libcxx/test/libcxx/type_traits/is_specialization.verify.cpp b/libcxx/test/libcxx/type_traits/is_specialization.verify.cpp
new file mode 100644
index 0000000000000..0dbdbf56f75c8
--- /dev/null
+++ b/libcxx/test/libcxx/type_traits/is_specialization.verify.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template <class _Tp, template <class...> class _Template>
+// inline constexpr bool __is_specialization_v = true if and only if _Tp is a specialization of _Template
+//
+// Tests the ill-formed instantiations.
+
+#include <type_traits>
+
+#include <array>
+#include <utility>
+
+// expected-error at +1 {{template template argument has 
diff erent template parameters than its corresponding template template parameter}}
+static_assert(!std::__is_specialization_v<std::pair<int, size_t>, std::array>);

diff  --git a/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
index 56b6692ac34bb..db953814d9b3e 100644
--- a/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
+++ b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp
@@ -185,7 +185,8 @@ void test_P1636() {
 #endif
   assert_is_not_formattable<std::shared_ptr<int>, CharT>();
 #ifndef TEST_HAS_NO_LOCALIZATION
-  assert_is_not_formattable<std::sub_match<CharT*>, CharT>();
+  if constexpr (!std::same_as<CharT, int>) // sub_match only works with proper character types
+    assert_is_not_formattable<std::sub_match<CharT*>, CharT>();
 #endif
 #ifndef TEST_HAS_NO_THREADS
   assert_is_not_formattable<std::thread::id, CharT>();

diff  --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtkind/format_kind.compile.pass.cpp b/libcxx/test/std/utilities/format/format.range/format.range.fmtkind/format_kind.compile.pass.cpp
new file mode 100644
index 0000000000000..d343ad1b1900b
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtkind/format_kind.compile.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+// 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, c++20
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+
+// <format>
+
+// template<ranges::input_range R>
+//     requires same_as<R, remove_cvref_t<R>>
+//  constexpr range_format format_kind<R> = see below;
+
+#include <format>
+
+#include <array>
+#include <deque>
+#include <format>
+#include <forward_list>
+#include <iterator>
+#include <list>
+#include <map>
+#include <ranges>
+#include <set>
+#include <span>
+#include <vector>
+#include <unordered_map>
+#include <unordered_set>
+#include <valarray>
+
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_FILESYSTEM_LIBRARY
+#  include <filesystem>
+#endif
+
+// [format.range.fmtkind]
+// If same_as<remove_cvref_t<ranges::range_reference_t<R>>, R> is true,
+// format_kind<R> is range_format::disabled.
+// [Note 1: This prevents constraint recursion for ranges whose reference type
+// is the same range type. For example, std::filesystem::path is a range of
+// std::filesystem::path. - end note]
+struct recursive_range {
+  struct iterator {
+    using iterator_concept = std::input_iterator_tag;
+    using value_type       = recursive_range;
+    using 
diff erence_type  = ptr
diff _t;
+    using reference        = recursive_range;
+
+    reference operator*() const;
+
+    iterator& operator++();
+    iterator operator++(int);
+
+    friend bool operator==(const iterator&, const iterator&);
+  };
+
+  iterator begin();
+  iterator end();
+};
+
+static_assert(std::ranges::input_range<recursive_range>, "format_kind requires an input range");
+static_assert(std::format_kind<recursive_range> == std::range_format::disabled);
+
+#ifndef TEST_HAS_NO_FILESYSTEM_LIBRARY
+static_assert(std::format_kind<std::filesystem::path> == std::range_format::disabled);
+#endif
+
+static_assert(std::format_kind<std::map<int, int>> == std::range_format::map);
+static_assert(std::format_kind<std::multimap<int, int>> == std::range_format::map);
+static_assert(std::format_kind<std::unordered_map<int, int>> == std::range_format::map);
+static_assert(std::format_kind<std::unordered_multimap<int, int>> == std::range_format::map);
+
+static_assert(std::format_kind<std::set<int>> == std::range_format::set);
+static_assert(std::format_kind<std::multiset<int>> == std::range_format::set);
+static_assert(std::format_kind<std::unordered_set<int>> == std::range_format::set);
+static_assert(std::format_kind<std::unordered_multiset<int>> == std::range_format::set);
+
+static_assert(std::format_kind<std::array<int, 1>> == std::range_format::sequence);
+static_assert(std::format_kind<std::vector<int>> == std::range_format::sequence);
+static_assert(std::format_kind<std::deque<int>> == std::range_format::sequence);
+static_assert(std::format_kind<std::forward_list<int>> == std::range_format::sequence);
+static_assert(std::format_kind<std::list<int>> == std::range_format::sequence);
+
+static_assert(std::format_kind<std::span<int>> == std::range_format::sequence);
+
+static_assert(std::format_kind<std::valarray<int>> == std::range_format::sequence);
+
+// [format.range.fmtkind]/3
+//   Remarks: Pursuant to [namespace.std], users may specialize format_kind for
+//   cv-unqualified program-defined types that model ranges::input_range. Such
+//   specializations shall be usable in constant expressions ([expr.const]) and
+//   have type const range_format.
+// Note only test the specializing, not all constraints.
+struct no_specialization : std::ranges::view_base {
+  using key_type = void;
+  int* begin() const;
+  int* end() const;
+};
+static_assert(std::format_kind<no_specialization> == std::range_format::set);
+
+// The struct's "contents" are the same as no_specialization.
+struct specialized : std::ranges::view_base {
+  using key_type = void;
+  int* begin() const;
+  int* end() const;
+};
+
+template <>
+constexpr std::range_format std::format_kind<specialized> = std::range_format::sequence;
+static_assert(std::format_kind<specialized> == std::range_format::sequence);

diff  --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtkind/format_kind.verify.cpp b/libcxx/test/std/utilities/format/format.range/format.range.fmtkind/format_kind.verify.cpp
new file mode 100644
index 0000000000000..f95ea235fdf40
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtkind/format_kind.verify.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+// 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, c++20
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+
+// <format>
+
+// template<ranges::input_range R>
+//   requires same_as<R, remove_cvref_t<R>>
+// constexpr range_format format_kind<R> = see below;
+
+#include <format>
+
+#include <array>
+#include <queue>
+#include <stack>
+#include <tuple>
+#include <utility>
+
+#include "test_macros.h"
+
+constexpr std::range_format valid = std::format_kind<std::array<int, 1>>;
+
+// expected-error@*:* {{create a template specialization of format_kind for your type}}
+constexpr std::range_format invalid_due_to_const = std::format_kind<const std::array<int, 1>>;
+
+// expected-error@*:* {{create a template specialization of format_kind for your type}}
+constexpr std::range_format invalid_due_to_volatile = std::format_kind<volatile std::array<int, 1>>;
+
+// expected-error@*:* {{create a template specialization of format_kind for your type}}
+constexpr std::range_format invalid_due_to_reference = std::format_kind<std::array<int, 1>&>;
+
+// expected-error@*:* {{create a template specialization of format_kind for your type}}
+constexpr std::range_format invalid_no_input_range = std::format_kind<int>;
+
+// expected-error@*:* {{create a template specialization of format_kind for your type}}
+constexpr std::range_format not_a_range_stack = std::format_kind<std::stack<int>>;
+
+// expected-error@*:* {{create a template specialization of format_kind for your type}}
+constexpr std::range_format not_a_range_queue = std::format_kind<std::queue<int>>;
+
+// expected-error@*:* {{create a template specialization of format_kind for your type}}
+constexpr std::range_format not_a_range_priority_queue = std::format_kind<std::priority_queue<int>>;
+
+// expected-error@*:* {{create a template specialization of format_kind for your type}}
+constexpr std::range_format not_a_range_pair = std::format_kind<std::pair<int, int>>;
+
+// expected-error@*:* {{create a template specialization of format_kind for your type}}
+constexpr std::range_format not_a_range_tuple_1 = std::format_kind<std::tuple<int>>;
+
+// expected-error@*:* {{create a template specialization of format_kind for your type}}
+constexpr std::range_format not_a_range_tuple_2 = std::format_kind<std::tuple<int, int>>;
+
+// expected-error@*:* {{create a template specialization of format_kind for your type}}
+constexpr std::range_format not_a_range_tuple_3 = std::format_kind<std::tuple<int, int, int>>;

diff  --git a/libcxx/test/std/utilities/format/format.range/format.range.fmtkind/range_format.compile.pass.cpp b/libcxx/test/std/utilities/format/format.range/format.range.fmtkind/range_format.compile.pass.cpp
new file mode 100644
index 0000000000000..df127d9d296c9
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.range/format.range.fmtkind/range_format.compile.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+// 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, c++20
+// UNSUPPORTED: libcpp-has-no-incomplete-format
+
+// <format>
+
+// enum class range_format {
+//   disabled,
+//   map,
+//   set,
+//   sequence,
+//   string,
+//   debug_string
+// };
+
+#include <format>
+
+// test that the enumeration values exist
+static_assert(requires { std::range_format::disabled; });
+static_assert(requires { std::range_format::map; });
+static_assert(requires { std::range_format::set; });
+static_assert(requires { std::range_format::sequence; });
+static_assert(requires { std::range_format::string; });
+static_assert(requires { std::range_format::debug_string; });


        


More information about the libcxx-commits mailing list