[libcxx-commits] [libcxx] 15c809e - [libc++][format] Adds a formattable concept.

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Wed May 18 11:02:15 PDT 2022


Author: Mark de Wever
Date: 2022-05-18T20:02:08+02:00
New Revision: 15c809e8e78083d59f1c0b09ca1d1644e0976961

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

LOG: [libc++][format] Adds a formattable concept.

The concept is based on P2286R2 Formatting Ranges. It will be used to
optimise the storage of __format_arg_store as required by LWG-3473.

Depends on D120916

Reviewed By: #libc, Mordante

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

Added: 
    libcxx/include/__format/concepts.h
    libcxx/test/libcxx/utilities/format/format.formatter/format.formatter.spec/formattable.compile.pass.cpp

Modified: 
    libcxx/include/CMakeLists.txt
    libcxx/include/__format/format_fwd.h
    libcxx/include/format
    libcxx/include/module.modulemap
    libcxx/test/libcxx/private_headers.verify.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 55ac4dbae0b7..2da8b9875810 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -209,6 +209,7 @@ set(files
   __filesystem/space_info.h
   __filesystem/u8path.h
   __format/buffer.h
+  __format/concepts.h
   __format/enable_insertable.h
   __format/format_arg.h
   __format/format_args.h

diff  --git a/libcxx/include/__format/concepts.h b/libcxx/include/__format/concepts.h
new file mode 100644
index 000000000000..8df6493b0aed
--- /dev/null
+++ b/libcxx/include/__format/concepts.h
@@ -0,0 +1,53 @@
+// -*- 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_CONCEPTS_H
+#define _LIBCPP___FORMAT_CONCEPTS_H
+
+#include <__concepts/same_as.h>
+#include <__concepts/semiregular.h>
+#include <__config>
+#include <__format/format_fwd.h>
+#include <__format/format_parse_context.h>
+#include <type_traits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 17
+
+// The output iterator isn't specified. A formatter should accept any
+// output_iterator. This iterator is a minimal iterator to test the concept.
+// (Note testing for (w)format_context would be a valid choice, but requires
+// selecting the proper one depending on the type of _CharT.)
+template <class _CharT>
+using __fmt_iter_for = _CharT*;
+
+// The concept is based on P2286R6
+// It lacks the const of __cf as required by, the not yet accepted, LWG-3636.
+// The current formatters can't be easily adapted, but that is WIP.
+// TODO FMT properly implement this concepts once accepted.
+template <class _Tp, class _CharT>
+concept __formattable = (semiregular<formatter<remove_cvref_t<_Tp>, _CharT>>) &&
+                        requires(formatter<remove_cvref_t<_Tp>, _CharT> __f,
+                                 formatter<remove_cvref_t<_Tp>, _CharT> __cf, _Tp __t,
+                                 basic_format_context<__fmt_iter_for<_CharT>, _CharT> __fc,
+                                 basic_format_parse_context<_CharT> __pc) {
+                          { __f.parse(__pc) } -> same_as<typename basic_format_parse_context<_CharT>::iterator>;
+                          { __cf.format(__t, __fc) } -> same_as<__fmt_iter_for<_CharT>>;
+                        };
+
+#endif //_LIBCPP_STD_VER > 17
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FORMAT_CONCEPTS_H

diff  --git a/libcxx/include/__format/format_fwd.h b/libcxx/include/__format/format_fwd.h
index 0f2e9085f881..908d10dbc3b6 100644
--- a/libcxx/include/__format/format_fwd.h
+++ b/libcxx/include/__format/format_fwd.h
@@ -12,6 +12,7 @@
 
 #include <__availability>
 #include <__config>
+#include <__iterator/concepts.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -27,6 +28,10 @@ class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_arg;
 template <class _Context, class... _Args>
 struct _LIBCPP_TEMPLATE_VIS __format_arg_store;
 
+template <class _OutIt, class _CharT>
+  requires output_iterator<_OutIt, const _CharT&>
+class _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT basic_format_context;
+
 template <class _Tp, class _CharT = char>
 struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter;
 

diff  --git a/libcxx/include/format b/libcxx/include/format
index 951627852fdb..070bfe286f0d 100644
--- a/libcxx/include/format
+++ b/libcxx/include/format
@@ -127,6 +127,7 @@ namespace std {
 #include <__config>
 #include <__debug>
 #include <__format/buffer.h>
+#include <__format/concepts.h>
 #include <__format/enable_insertable.h>
 #include <__format/format_arg.h>
 #include <__format/format_args.h>

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 7ae3d106ed5f..ef97b81e0fce 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -542,6 +542,7 @@ module std [system] {
 
     module __format {
       module buffer                   { private header "__format/buffer.h" }
+      module concepts                 { private header "__format/concepts.h" }
       module enable_insertable        { private header "__format/enable_insertable.h" }
       module format_arg               { private header "__format/format_arg.h" }
       module format_args              { private header "__format/format_args.h" }

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 58165caff3f2..cfeddfa4411f 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -241,6 +241,7 @@ END-SCRIPT
 #include <__filesystem/space_info.h> // expected-error@*:* {{use of private header from outside its module: '__filesystem/space_info.h'}}
 #include <__filesystem/u8path.h> // expected-error@*:* {{use of private header from outside its module: '__filesystem/u8path.h'}}
 #include <__format/buffer.h> // expected-error@*:* {{use of private header from outside its module: '__format/buffer.h'}}
+#include <__format/concepts.h> // expected-error@*:* {{use of private header from outside its module: '__format/concepts.h'}}
 #include <__format/enable_insertable.h> // expected-error@*:* {{use of private header from outside its module: '__format/enable_insertable.h'}}
 #include <__format/format_arg.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_arg.h'}}
 #include <__format/format_args.h> // expected-error@*:* {{use of private header from outside its module: '__format/format_args.h'}}

diff  --git a/libcxx/test/libcxx/utilities/format/format.formatter/format.formatter.spec/formattable.compile.pass.cpp b/libcxx/test/libcxx/utilities/format/format.formatter/format.formatter.spec/formattable.compile.pass.cpp
new file mode 100644
index 000000000000..cc3c3437dc77
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/format/format.formatter/format.formatter.spec/formattable.compile.pass.cpp
@@ -0,0 +1,328 @@
+//===----------------------------------------------------------------------===//
+// 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-has-no-incomplete-format
+
+// TODO FMT Move to std after P2286 has been accepted.
+
+// <format>
+
+// template<class T, class charT>
+// concept formattable = ...
+
+#include <array>
+#include <bitset>
+#include <bitset>
+#include <chrono>
+#include <complex>
+#include <concepts>
+#include <deque>
+#include <format>
+#include <forward_list>
+#include <list>
+#include <memory>
+#include <map>
+#include <optional>
+#include <queue>
+#include <set>
+#include <stack>
+#include <span>
+#include <tuple>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <valarray>
+#include <variant>
+
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_FILESYSTEM_LIBRARY
+#  include <filesystem>
+#endif
+#ifndef TEST_HAS_NO_LOCALIZATION
+#  include <regex>
+#endif
+#ifndef TEST_HAS_NO_THREADS
+#  include <thread>
+#endif
+
+template <class T, class CharT>
+void assert_is_not_formattable() {
+  static_assert(!std::__formattable<T, CharT>);
+}
+
+template <class T, class CharT>
+void assert_is_formattable() {
+  // Only formatters for CharT == char || CharT == wchar_t are enabled for the
+  // standard formatters. When CharT is a 
diff erent type the formatter should
+  // be disabled.
+  if constexpr (std::same_as<CharT, char>
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+                || std::same_as<CharT, wchar_t>
+#endif
+  )
+    static_assert(std::__formattable<T, CharT>);
+  else
+    assert_is_not_formattable<T, CharT>();
+}
+
+// Tests for P0645 Text Formatting
+template <class CharT>
+void test_P0645() {
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  // Tests the special formatter that converts a char to a wchar_t.
+  assert_is_formattable<char, wchar_t>();
+#endif
+  assert_is_formattable<CharT, CharT>();
+
+  assert_is_formattable<CharT*, CharT>();
+  assert_is_formattable<const CharT*, CharT>();
+  assert_is_formattable<std::basic_string<CharT>, CharT>();
+  assert_is_formattable<std::basic_string_view<CharT>, CharT>();
+
+  assert_is_formattable<bool, CharT>();
+
+  assert_is_formattable<signed char, CharT>();
+  assert_is_formattable<signed short, CharT>();
+  assert_is_formattable<signed int, CharT>();
+  assert_is_formattable<signed long, CharT>();
+  assert_is_formattable<signed long long, CharT>();
+#ifndef TEST_HAS_NO_INT128
+  assert_is_formattable<__int128_t, CharT>();
+#endif
+
+  assert_is_formattable<unsigned char, CharT>();
+  assert_is_formattable<unsigned short, CharT>();
+  assert_is_formattable<unsigned int, CharT>();
+  assert_is_formattable<unsigned long, CharT>();
+  assert_is_formattable<unsigned long long, CharT>();
+#ifndef TEST_HAS_NO_INT128
+  assert_is_formattable<__uint128_t, CharT>();
+#endif
+
+  assert_is_formattable<float, CharT>();
+  assert_is_formattable<double, CharT>();
+  assert_is_formattable<long double, CharT>();
+
+  assert_is_formattable<std::nullptr_t, CharT>();
+  assert_is_formattable<void*, CharT>();
+  assert_is_formattable<const void*, CharT>();
+}
+
+// Tests for P1361 Integration of chrono with text formatting
+//
+// Some tests are commented out since these types haven't been implemented in
+// chrono yet. After P1361 has been implemented these formatters should be all
+// enabled.
+template <class CharT>
+void test_P1361() {
+  assert_is_not_formattable<std::chrono::microseconds, CharT>();
+
+  assert_is_not_formattable<std::chrono::sys_time<std::chrono::microseconds>, CharT>();
+  //assert_is_formattable<std::chrono::utc_time<std::chrono::microseconds>, CharT>();
+  //assert_is_formattable<std::chrono::tai_time<std::chrono::microseconds>, CharT>();
+  //assert_is_formattable<std::chrono::gps_time<std::chrono::microseconds>, CharT>();
+  assert_is_not_formattable<std::chrono::file_time<std::chrono::microseconds>, CharT>();
+  assert_is_not_formattable<std::chrono::local_time<std::chrono::microseconds>, CharT>();
+
+  assert_is_not_formattable<std::chrono::day, CharT>();
+  assert_is_not_formattable<std::chrono::month, CharT>();
+  assert_is_not_formattable<std::chrono::year, CharT>();
+
+  assert_is_not_formattable<std::chrono::weekday, CharT>();
+  assert_is_not_formattable<std::chrono::weekday_indexed, CharT>();
+  assert_is_not_formattable<std::chrono::weekday_last, CharT>();
+
+  assert_is_not_formattable<std::chrono::month_day, CharT>();
+  assert_is_not_formattable<std::chrono::month_day_last, CharT>();
+  assert_is_not_formattable<std::chrono::month_weekday, CharT>();
+  assert_is_not_formattable<std::chrono::month_weekday_last, CharT>();
+
+  assert_is_not_formattable<std::chrono::year_month, CharT>();
+  assert_is_not_formattable<std::chrono::year_month_day, CharT>();
+  assert_is_not_formattable<std::chrono::year_month_day_last, CharT>();
+  assert_is_not_formattable<std::chrono::year_month_weekday, CharT>();
+  assert_is_not_formattable<std::chrono::year_month_weekday_last, CharT>();
+
+  assert_is_not_formattable<std::chrono::hh_mm_ss<std::chrono::microseconds>, CharT>();
+
+  //assert_is_formattable<std::chrono::sys_info, CharT>();
+  //assert_is_formattable<std::chrono::local_info, CharT>();
+
+  //assert_is_formattable<std::chrono::zoned_time, CharT>();
+}
+
+// Tests for P1636 Formatters for library types
+//
+// The paper hasn't been voted in so currently all formatters are disabled.
+// TODO validate whether the test is correct after the paper has been accepted.
+template <class CharT>
+void test_P1636() {
+  assert_is_not_formattable<std::basic_streambuf<CharT>, CharT>();
+  assert_is_not_formattable<std::bitset<42>, CharT>();
+  assert_is_not_formattable<std::complex<double>, CharT>();
+  assert_is_not_formattable<std::error_code, CharT>();
+#ifndef TEST_HAS_NO_FILESYSTEM_LIBRARY
+  assert_is_not_formattable<std::filesystem::path, CharT>();
+#endif
+  assert_is_not_formattable<std::shared_ptr<int>, CharT>();
+#ifndef TEST_HAS_NO_LOCALIZATION
+  assert_is_not_formattable<std::sub_match<CharT*>, CharT>();
+#endif
+#ifndef TEST_HAS_NO_THREADS
+  assert_is_not_formattable<std::thread::id, CharT>();
+#endif
+  assert_is_not_formattable<std::unique_ptr<int>, CharT>();
+}
+
+// Tests for P2286 Formatting ranges
+//
+// The paper hasn't been voted in so currently all formatters are disabled.
+// TODO validate whether the test is correct after the paper has been accepted.
+template <class CharT>
+void test_P2286() {
+  assert_is_not_formattable<std::array<int, 42>, CharT>();
+  assert_is_not_formattable<std::vector<int>, CharT>();
+  assert_is_not_formattable<std::deque<int>, CharT>();
+  assert_is_not_formattable<std::forward_list<int>, CharT>();
+  assert_is_not_formattable<std::list<int>, CharT>();
+
+  assert_is_not_formattable<std::set<int>, CharT>();
+  assert_is_not_formattable<std::map<int, int>, CharT>();
+  assert_is_not_formattable<std::multiset<int>, CharT>();
+  assert_is_not_formattable<std::multimap<int, int>, CharT>();
+
+  assert_is_not_formattable<std::unordered_set<int>, CharT>();
+  assert_is_not_formattable<std::unordered_map<int, int>, CharT>();
+  assert_is_not_formattable<std::unordered_multiset<int>, CharT>();
+  assert_is_not_formattable<std::unordered_multimap<int, int>, CharT>();
+
+  assert_is_not_formattable<std::stack<int>, CharT>();
+  assert_is_not_formattable<std::queue<int>, CharT>();
+  assert_is_not_formattable<std::priority_queue<int>, CharT>();
+
+  assert_is_not_formattable<std::span<int>, CharT>();
+
+  assert_is_not_formattable<std::valarray<int>, CharT>();
+
+  assert_is_not_formattable<std::pair<int, int>, CharT>();
+  assert_is_not_formattable<std::tuple<int>, CharT>();
+}
+
+class c {
+  void f();
+  void fc() const;
+  static void sf();
+};
+enum e { a };
+enum class ec { a };
+template <class CharT>
+void test_disabled() {
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  assert_is_not_formattable<const char*, wchar_t>();
+#endif
+  assert_is_not_formattable<const char*, char8_t>();
+  assert_is_not_formattable<const char*, char16_t>();
+  assert_is_not_formattable<const char*, char32_t>();
+
+  assert_is_not_formattable<c, CharT>();
+  assert_is_not_formattable<const c, CharT>();
+  assert_is_not_formattable<volatile c, CharT>();
+  assert_is_not_formattable<const volatile c, CharT>();
+
+  assert_is_not_formattable<e, CharT>();
+  assert_is_not_formattable<const e, CharT>();
+  assert_is_not_formattable<volatile e, CharT>();
+  assert_is_not_formattable<const volatile e, CharT>();
+
+  assert_is_not_formattable<ec, CharT>();
+  assert_is_not_formattable<const ec, CharT>();
+  assert_is_not_formattable<volatile ec, CharT>();
+  assert_is_not_formattable<const volatile ec, CharT>();
+
+  assert_is_not_formattable<int*, CharT>();
+  assert_is_not_formattable<const int*, CharT>();
+  assert_is_not_formattable<volatile int*, CharT>();
+  assert_is_not_formattable<const volatile int*, CharT>();
+
+  assert_is_not_formattable<c*, CharT>();
+  assert_is_not_formattable<const c*, CharT>();
+  assert_is_not_formattable<volatile c*, CharT>();
+  assert_is_not_formattable<const volatile c*, CharT>();
+
+  assert_is_not_formattable<e*, CharT>();
+  assert_is_not_formattable<const e*, CharT>();
+  assert_is_not_formattable<volatile e*, CharT>();
+  assert_is_not_formattable<const volatile e*, CharT>();
+
+  assert_is_not_formattable<ec*, CharT>();
+  assert_is_not_formattable<const ec*, CharT>();
+  assert_is_not_formattable<volatile ec*, CharT>();
+  assert_is_not_formattable<const volatile ec*, CharT>();
+
+  assert_is_not_formattable<void (*)(), CharT>();
+  assert_is_not_formattable<void (c::*)(), CharT>();
+  assert_is_not_formattable<void (c::*)() const, CharT>();
+
+  assert_is_not_formattable<std::optional<int>, CharT>();
+  assert_is_not_formattable<std::variant<int>, CharT>();
+
+  assert_is_not_formattable<std::shared_ptr<c>, CharT>();
+  assert_is_not_formattable<std::unique_ptr<c>, CharT>();
+
+  assert_is_not_formattable<std::array<c, 42>, CharT>();
+  assert_is_not_formattable<std::vector<c>, CharT>();
+  assert_is_not_formattable<std::deque<c>, CharT>();
+  assert_is_not_formattable<std::forward_list<c>, CharT>();
+  assert_is_not_formattable<std::list<c>, CharT>();
+
+  assert_is_not_formattable<std::set<c>, CharT>();
+  assert_is_not_formattable<std::map<c, int>, CharT>();
+  assert_is_not_formattable<std::multiset<c>, CharT>();
+  assert_is_not_formattable<std::multimap<c, int>, CharT>();
+
+  assert_is_not_formattable<std::unordered_set<c>, CharT>();
+  assert_is_not_formattable<std::unordered_map<c, int>, CharT>();
+  assert_is_not_formattable<std::unordered_multiset<c>, CharT>();
+  assert_is_not_formattable<std::unordered_multimap<c, int>, CharT>();
+
+  assert_is_not_formattable<std::stack<c>, CharT>();
+  assert_is_not_formattable<std::queue<c>, CharT>();
+  assert_is_not_formattable<std::priority_queue<c>, CharT>();
+
+  assert_is_not_formattable<std::span<c>, CharT>();
+
+  assert_is_not_formattable<std::valarray<c>, CharT>();
+
+  assert_is_not_formattable<std::pair<c, int>, CharT>();
+  assert_is_not_formattable<std::tuple<c>, CharT>();
+
+  assert_is_not_formattable<std::optional<c>, CharT>();
+  assert_is_not_formattable<std::variant<c>, CharT>();
+}
+
+template <class CharT>
+void test() {
+  test_P0645<CharT>();
+  test_P1361<CharT>();
+  test_P1636<CharT>();
+  test_P2286<CharT>();
+  test_disabled<CharT>();
+}
+
+void test() {
+  test<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>();
+#endif
+  test<char8_t>();
+  test<char16_t>();
+  test<char32_t>();
+
+  test<int>();
+}


        


More information about the libcxx-commits mailing list