Skip to content

[libc++][format] P2637R3: Member visit (std::basic_format_arg) #76449

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 44 commits into from
Jan 21, 2024

Conversation

H-G-Hristov
Copy link
Contributor

@H-G-Hristov H-G-Hristov commented Dec 27, 2023

Implements parts of: P2637R3 https://wg21.link/P2637R3 (https://eel.is/c++draft/variant.visit)

Implements:
basic_format_arg.visit()
basic_format_arg.visit<R>()
Deprecates:
std::visit_format_arg()

The tests are as close as possible to the non-member function tests.

To land after: #76447, #76268

Copy link

github-actions bot commented Dec 27, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

@Zingam Zingam added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Dec 27, 2023
@llvmbot
Copy link
Member

llvmbot commented Dec 27, 2023

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

Changes

Implements parts of: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2637r3.html (https://eel.is/c++draft/variant.visit)

Adds member visit tests and (NFC) refactors + reformats the non-member visit tests to accomodate the member visit additions for consistency.


Patch is 33.84 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/76449.diff

8 Files Affected:

  • (modified) libcxx/docs/Status/Cxx2cPapers.csv (+1-1)
  • (modified) libcxx/include/__config (+6)
  • (modified) libcxx/include/__format/format_arg.h (+66-2)
  • (modified) libcxx/include/format (+1-1)
  • (renamed) libcxx/test/std/utilities/format/format.arguments/format.arg/visit.pass.cpp (+68-60)
  • (added) libcxx/test/std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp (+326)
  • (added) libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.deprecated.verify.cpp (+64)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (+1)
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index ff83648aa76830..b33543aad8b945 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -17,7 +17,7 @@
 "`P0792R14 <https://wg21.link/P0792R14>`__","LWG","``function_ref``: a type-erased callable reference","Varna June 2023","","",""
 "`P2874R2 <https://wg21.link/P2874R2>`__","LWG","Mandating Annex D Require No More","Varna June 2023","","",""
 "`P2757R3 <https://wg21.link/P2757R3>`__","LWG","Type-checking format args","Varna June 2023","","","|format|"
-"`P2637R3 <https://wg21.link/P2637R3>`__","LWG","Member ``visit``","Varna June 2023","","","|format|"
+"`P2637R3 <https://wg21.link/P2637R3>`__","LWG","Member ``visit``","Varna June 2023","|Complete|","18.0",""
 "`P2641R4 <https://wg21.link/P2641R4>`__","CWG, LWG","Checking if a ``union`` alternative is active","Varna June 2023","","",""
 "`P1759R6 <https://wg21.link/P1759R6>`__","LWG","Native handles and file streams","Varna June 2023","","",""
 "`P2697R1 <https://wg21.link/P2697R1>`__","LWG","Interfacing ``bitset`` with ``string_view``","Varna June 2023","|Complete|","18.0",""
diff --git a/libcxx/include/__config b/libcxx/include/__config
index adff13e714cb64..ee85b73c2e4cfe 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -956,6 +956,12 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_DEPRECATED_IN_CXX23
 #  endif
 
+#  if _LIBCPP_STD_VER >= 26
+#    define _LIBCPP_DEPRECATED_IN_CXX26 _LIBCPP_DEPRECATED
+#  else
+#    define _LIBCPP_DEPRECATED_IN_CXX26
+#  endif
+
 #  if !defined(_LIBCPP_HAS_NO_CHAR8_T)
 #    define _LIBCPP_DEPRECATED_WITH_CHAR8_T _LIBCPP_DEPRECATED
 #  else
diff --git a/libcxx/include/__format/format_arg.h b/libcxx/include/__format/format_arg.h
index 280c9108241754..bc9e13eb9029a5 100644
--- a/libcxx/include/__format/format_arg.h
+++ b/libcxx/include/__format/format_arg.h
@@ -93,7 +93,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr __arg_t __get_packed_type(uint64_t __types, size
 
 } // namespace __format
 
-// This function is not user obervable, so it can directly use the non-standard
+// This function is not user observable, so it can directly use the non-standard
 // types of the "variant". See __arg_t for more details.
 template <class _Visitor, class _Context>
 _LIBCPP_HIDE_FROM_ABI decltype(auto) __visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) {
@@ -144,6 +144,57 @@ _LIBCPP_HIDE_FROM_ABI decltype(auto) __visit_format_arg(_Visitor&& __vis, basic_
   __libcpp_unreachable();
 }
 
+#  if _LIBCPP_STD_VER >= 26
+template <class _Rp, class _Visitor, class _Context>
+_LIBCPP_HIDE_FROM_ABI _Rp __visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) {
+  switch (__arg.__type_) {
+  case __format::__arg_t::__none:
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__monostate_);
+  case __format::__arg_t::__boolean:
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__boolean_);
+  case __format::__arg_t::__char_type:
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__char_type_);
+  case __format::__arg_t::__int:
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__int_);
+  case __format::__arg_t::__long_long:
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__long_long_);
+  case __format::__arg_t::__i128:
+#    ifndef _LIBCPP_HAS_NO_INT128
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__i128_);
+#    else
+    __libcpp_unreachable();
+#    endif
+  case __format::__arg_t::__unsigned:
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__unsigned_);
+  case __format::__arg_t::__unsigned_long_long:
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__unsigned_long_long_);
+  case __format::__arg_t::__u128:
+#    ifndef _LIBCPP_HAS_NO_INT128
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__u128_);
+#    else
+    __libcpp_unreachable();
+#    endif
+  case __format::__arg_t::__float:
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__float_);
+  case __format::__arg_t::__double:
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__double_);
+  case __format::__arg_t::__long_double:
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__long_double_);
+  case __format::__arg_t::__const_char_type_ptr:
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__const_char_type_ptr_);
+  case __format::__arg_t::__string_view:
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__string_view_);
+  case __format::__arg_t::__ptr:
+    return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__ptr_);
+  case __format::__arg_t::__handle:
+    return std::invoke_r<_Rp>(
+        std::forward<_Visitor>(__vis), typename basic_format_arg<_Context>::handle{__arg.__value_.__handle_});
+  }
+
+  __libcpp_unreachable();
+}
+#  endif
+
 /// Contains the values used in basic_format_arg.
 ///
 /// This is a separate type so it's possible to store the values and types in
@@ -227,6 +278,18 @@ class _LIBCPP_TEMPLATE_VIS basic_format_arg {
 
   _LIBCPP_HIDE_FROM_ABI explicit operator bool() const noexcept { return __type_ != __format::__arg_t::__none; }
 
+#  if _LIBCPP_STD_VER >= 26
+  template <class _Visitor>
+  _LIBCPP_HIDE_FROM_ABI decltype(auto) visit(this basic_format_arg __arg, _Visitor&& __vis) {
+    return std::__visit_format_arg(std::forward<_Visitor>(__vis), __arg);
+  }
+
+  template <class _Rp, class _Visitor>
+  _LIBCPP_HIDE_FROM_ABI _Rp visit(this basic_format_arg __arg, _Visitor&& __vis) {
+    return std::__visit_format_arg<_Rp>(std::forward<_Visitor>(__vis), __arg);
+  }
+#  endif
+
 private:
   using char_type = typename _Context::char_type;
 
@@ -267,7 +330,8 @@ class _LIBCPP_TEMPLATE_VIS basic_format_arg<_Context>::handle {
 // This function is user facing, so it must wrap the non-standard types of
 // the "variant" in a handle to stay conforming. See __arg_t for more details.
 template <class _Visitor, class _Context>
-_LIBCPP_HIDE_FROM_ABI decltype(auto) visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) {
+_LIBCPP_DEPRECATED_IN_CXX26 _LIBCPP_HIDE_FROM_ABI decltype(auto)
+visit_format_arg(_Visitor&& __vis, basic_format_arg<_Context> __arg) {
   switch (__arg.__type_) {
 #  ifndef _LIBCPP_HAS_NO_INT128
   case __format::__arg_t::__i128: {
diff --git a/libcxx/include/format b/libcxx/include/format
index ab9b336d0cdabe..e1566fb4636a7b 100644
--- a/libcxx/include/format
+++ b/libcxx/include/format
@@ -170,7 +170,7 @@ namespace std {
   template<class Context> class basic_format_arg;
 
   template<class Visitor, class Context>
-    see below visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg);
+    see below visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg); // deprecated in C++26
 
   // [format.arg.store], class template format-arg-store
   template<class Context, class... Args> struct format-arg-store;      // exposition only
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.pass.cpp
similarity index 77%
rename from libcxx/test/std/utilities/format/format.arguments/format.arg/visit_format_arg.pass.cpp
rename to libcxx/test/std/utilities/format/format.arguments/format.arg/visit.pass.cpp
index 3ddf2d0ff732a7..c3b1609c159c8d 100644
--- 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.pass.cpp
@@ -10,18 +10,26 @@
 
 // <format>
 
+// class basic_format_arg;
+
+// template<class Visitor>
+//   decltype(auto) visit(this basic_format_arg arg, Visitor&& vis);  // since C++26
+
 // template<class Visitor, class Context>
-//   see below visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg);
+//   see below visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg); // deprecated in C++26
 
 #include <algorithm>
-#include <format>
 #include <cassert>
+#include <format>
 #include <type_traits>
 
 #include "constexpr_char_traits.h"
-#include "test_macros.h"
 #include "make_string.h"
 #include "min_allocator.h"
+#include "test_macros.h"
+
+// Deprecated `std::visit_format_arg` should be tested in C++26 or newer.
+TEST_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-declarations")
 
 template <class Context, class To, class From>
 void test(From value) {
@@ -31,20 +39,41 @@ void test(From value) {
   LIBCPP_ASSERT(format_args.__size() == 1);
   assert(format_args.get(0));
 
-  auto result = std::visit_format_arg(
-      [v = To(value)](auto a) -> To {
-        if constexpr (std::is_same_v<To, decltype(a)>) {
-          assert(v == a);
-          return a;
-        } else {
-          assert(false);
-          return {};
-        }
-      },
-      format_args.get(0));
+#if _LIBCPP_STD_VER >= 26
+  // member
+  {
+    auto result = format_args.get(0).visit([v = To(value)](auto a) -> To {
+      if constexpr (std::is_same_v<To, decltype(a)>) {
+        assert(v == a);
+        return a;
+      } else {
+        assert(false);
+        return {};
+      }
+    });
+
+    using ct = std::common_type_t<From, To>;
+    assert(static_cast<ct>(result) == static_cast<ct>(value));
+  }
+#endif
 
-  using ct = std::common_type_t<From, To>;
-  assert(static_cast<ct>(result) == static_cast<ct>(value));
+  // non-member
+  {
+    auto result = std::visit_format_arg(
+        [v = To(value)](auto a) -> To {
+          if constexpr (std::is_same_v<To, decltype(a)>) {
+            assert(v == a);
+            return a;
+          } else {
+            assert(false);
+            return {};
+          }
+        },
+        format_args.get(0));
+
+    using ct = std::common_type_t<From, To>;
+    assert(static_cast<ct>(result) == static_cast<ct>(value));
+  }
 }
 
 // Some types, as an extension, are stored in the variant. The Standard
@@ -75,8 +104,8 @@ void test_string_view(From value) {
   assert(format_args.get(0));
 
   using CharT = typename Context::char_type;
-  using To = std::basic_string_view<CharT>;
-  using V = std::basic_string<CharT>;
+  using To    = std::basic_string_view<CharT>;
+  using V     = std::basic_string<CharT>;
   auto result = std::visit_format_arg(
       [v = V(value.begin(), value.end())](auto a) -> To {
         if constexpr (std::is_same_v<To, decltype(a)>) {
@@ -170,8 +199,7 @@ void test() {
   test<Context, int, int>(std::numeric_limits<short>::max());
   test<Context, int, int>(std::numeric_limits<int>::max());
 
-  using LongToType =
-      std::conditional_t<sizeof(long) == sizeof(int), int, long long>;
+  using LongToType = std::conditional_t<sizeof(long) == sizeof(int), int, long long>;
 
   test<Context, LongToType, long>(std::numeric_limits<long>::min());
   test<Context, LongToType, long>(std::numeric_limits<int>::min());
@@ -202,14 +230,11 @@ void test() {
   // Test unsigned integer types.
 
   test<Context, unsigned, unsigned char>(0);
-  test<Context, unsigned, unsigned char>(
-      std::numeric_limits<unsigned char>::max());
+  test<Context, unsigned, unsigned char>(std::numeric_limits<unsigned char>::max());
 
   test<Context, unsigned, unsigned short>(0);
-  test<Context, unsigned, unsigned short>(
-      std::numeric_limits<unsigned char>::max());
-  test<Context, unsigned, unsigned short>(
-      std::numeric_limits<unsigned short>::max());
+  test<Context, unsigned, unsigned short>(std::numeric_limits<unsigned char>::max());
+  test<Context, unsigned, unsigned short>(std::numeric_limits<unsigned short>::max());
 
   test<Context, unsigned, unsigned>(0);
   test<Context, unsigned, unsigned>(std::numeric_limits<unsigned char>::max());
@@ -217,30 +242,20 @@ void test() {
   test<Context, unsigned, unsigned>(std::numeric_limits<unsigned>::max());
 
   using UnsignedLongToType =
-      std::conditional_t<sizeof(unsigned long) == sizeof(unsigned), unsigned,
-                         unsigned long long>;
+      std::conditional_t<sizeof(unsigned long) == sizeof(unsigned), unsigned, unsigned long long>;
 
   test<Context, UnsignedLongToType, unsigned long>(0);
-  test<Context, UnsignedLongToType, unsigned long>(
-      std::numeric_limits<unsigned char>::max());
-  test<Context, UnsignedLongToType, unsigned long>(
-      std::numeric_limits<unsigned short>::max());
-  test<Context, UnsignedLongToType, unsigned long>(
-      std::numeric_limits<unsigned>::max());
-  test<Context, UnsignedLongToType, unsigned long>(
-      std::numeric_limits<unsigned long>::max());
+  test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned char>::max());
+  test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned short>::max());
+  test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned>::max());
+  test<Context, UnsignedLongToType, unsigned long>(std::numeric_limits<unsigned long>::max());
 
   test<Context, unsigned long long, unsigned long long>(0);
-  test<Context, unsigned long long, unsigned long long>(
-      std::numeric_limits<unsigned char>::max());
-  test<Context, unsigned long long, unsigned long long>(
-      std::numeric_limits<unsigned short>::max());
-  test<Context, unsigned long long, unsigned long long>(
-      std::numeric_limits<unsigned>::max());
-  test<Context, unsigned long long, unsigned long long>(
-      std::numeric_limits<unsigned long>::max());
-  test<Context, unsigned long long, unsigned long long>(
-      std::numeric_limits<unsigned long long>::max());
+  test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned char>::max());
+  test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned short>::max());
+  test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned>::max());
+  test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned long>::max());
+  test<Context, unsigned long long, unsigned long long>(std::numeric_limits<unsigned long long>::max());
 
 #ifndef TEST_HAS_NO_INT128
   test_handle<Context, __uint128_t>(0);
@@ -262,16 +277,12 @@ void test() {
   test<Context, double, double>(std::numeric_limits<double>::min());
   test<Context, double, double>(std::numeric_limits<double>::max());
 
-  test<Context, long double, long double>(
-      -std::numeric_limits<long double>::max());
-  test<Context, long double, long double>(
-      -std::numeric_limits<long double>::min());
+  test<Context, long double, long double>(-std::numeric_limits<long double>::max());
+  test<Context, long double, long double>(-std::numeric_limits<long double>::min());
   test<Context, long double, long double>(-0.0);
   test<Context, long double, long double>(0.0);
-  test<Context, long double, long double>(
-      std::numeric_limits<long double>::min());
-  test<Context, long double, long double>(
-      std::numeric_limits<long double>::max());
+  test<Context, long double, long double>(std::numeric_limits<long double>::min());
+  test<Context, long double, long double>(std::numeric_limits<long double>::max());
 
   // Test const CharT pointer types.
 
@@ -307,8 +318,7 @@ void test() {
   }
 
   {
-    using From = std::basic_string<CharT, constexpr_char_traits<CharT>,
-                                   std::allocator<CharT>>;
+    using From = std::basic_string<CharT, constexpr_char_traits<CharT>, std::allocator<CharT>>;
 
     test_string_view<Context>(From());
     test_string_view<Context>(From(empty.c_str()));
@@ -316,8 +326,7 @@ void test() {
   }
 
   {
-    using From =
-        std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>>;
+    using From = std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>>;
 
     test_string_view<Context>(From());
     test_string_view<Context>(From(empty.c_str()));
@@ -325,8 +334,7 @@ void test() {
   }
 
   {
-    using From = std::basic_string<CharT, constexpr_char_traits<CharT>,
-                                   min_allocator<CharT>>;
+    using From = std::basic_string<CharT, constexpr_char_traits<CharT>, min_allocator<CharT>>;
 
     test_string_view<Context>(From());
     test_string_view<Context>(From(empty.c_str()));
diff --git a/libcxx/test/std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp b/libcxx/test/std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp
new file mode 100644
index 00000000000000..470df35ef97196
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp
@@ -0,0 +1,326 @@
+//===----------------------------------------------------------------------===//
+// 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, c++23
+// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
+
+// <format>
+
+// class basic_format_arg;
+
+// template<class R, class Visitor>
+//   R visit(this basic_format_arg arg, Visitor&& vis);
+
+#include <algorithm>
+#include <cassert>
+#include <format>
+#include <type_traits>
+
+#include "constexpr_char_traits.h"
+#include "make_string.h"
+#include "min_allocator.h"
+#include "test_macros.h"
+
+// The expected result type shouldn't matter,therefore use a hardcoded value for simplicity.
+using ExpectedResultType = bool;
+constexpr ExpectedResultType visited{true};
+
+template <class ExpectedR>
+ExpectedR make_expected_result() {
+  if constexpr (std::is_same_v<ExpectedR, bool>) {
+    return true;
+  } else if constexpr (std::is_same_v<ExpectedR, long>) {
+    return 192812079084L;
+  } else {
+    return "visited";
+  }
+}
+
+template <class Context, class To, class ExpectedR, class From>
+void test(From value, const ExpectedR& expectedValue) {
+  auto store = std::make_format_args<Context>(value);
+  std::basic_format_args<Context> format_args{store};
+
+  LIBCPP_ASSERT(format_args.__size() == 1);
+  assert(format_args.get(0));
+
+  // member
+  {
+    std::same_as<ExpectedR> decltype(auto) result =
+        format_args.get(0).template visit<ExpectedR>([v = To(value)](auto a) -> ExpectedR {
+          if constexpr (std::is_same_v<To, decltype(a)>) {
+            assert(v == a);
+            return make_expected_result<ExpectedR>();
+          } else {
+            assert(false);
+            return {};
+          }
+        });
+
+    assert(result == expectedValue);
+  }
+}
+
+// Some types, as an extension, are stored in the variant. The Standard
+// requires them to be observed as a handle.
+template <class Context, class T, class ExpectedR>
+void test_handle(T value, ExpectedR expectedValue) {
+  auto store = std::make_format_args<Context>(value);
+  std::basic_format_args<Context> format_args{store};
+
+  LIBCPP_ASSERT(format_args.__size() == 1);
+  assert(format_args.get(0));
+
+  std::same_as<ExpectedR> decltype(auto) result = format_args.get(0).template visit<ExpectedR>([](auto a) -> ExpectedR {
+    // TODO: This check fails
+    (void)a;
+    // assert((std::is_same_v<decltype(a), typename std::basic_format_arg<Context>::handle>));
+
+    return make_expected_result<ExpectedR>();
+  });
+
+  assert(result == expectedValue);
+}
+
+// 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 <class Context, class ExpectedR, class From>
+void test_string_view(From value, ExpectedR expectedValue) {
+  auto store = std::make_format_args<Context>(value);
+  std::basic_format_args<Context> format_args{store};
+
+  LIBCPP_ASSERT(format_args.__size() == 1);
+  assert(format_args.get(0));
+
+  using CharT = typename Context::char_type;
+  using To    = std::basic_string_view<CharT>;
+  using V     = std::basic_string<CharT>;
+
+  std::same_as<ExpectedR> decltype(auto) result =
+      format_args.get(0).template visit<ExpectedR>([v = V(value.begin(), value.end())](auto a) -> ExpectedR {
+        if constexpr (std::is_same_v<To, decltype(a)>) {...
[truncated]

@Zingam
Copy link
Contributor

Zingam commented Dec 28, 2023

@mordante I have three issues:

  1. I disabled the deprecation warning or visit_format_arg() in the visit.pass.cpp file with TEST_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-declarations"). But many more tests are failing, even in tests (IMO) which are not related directly. What do I need to do?
  2. The declaration of visit uses deducing this, which is available in Clang 18 only. As far I can tell the complication fails due to clang-tidy 17 being used in the CI.
  3. The test of the handle for visit<R>() is failing, so I have skipped it for now.

@Zingam Zingam changed the title [libc++][format] P2637R3: Member visit [libc++][format] P2637R3: Member visit (std::basic_format_arg) Dec 28, 2023
@mordante mordante self-assigned this Dec 28, 2023
Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mordante I have three issues:

1. I disabled the deprecation warning or `visit_format_arg()` in the `visit.pass.cpp` file with `TEST_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-declarations")`. But many more tests are failing, even in tests (IMO) which are not related directly. What do I need to do?

Can you post links to the failed tests? Preferably on Discord, that makes the discussion easier.

2. The declaration of `visit` uses `deducing this`, which is available in Clang 18 only. As far I can tell the complication fails due to `clang-tidy 17` being used in the CI.

I recently ran into the same issue, we probably should suggest moving the the latest clang-tidy in the CI. I expect you need to disable some tests for older clang versions too and maybe in the code guard the code with the appropriate compiler FTM.

3. The test of the `handle` for `visit<R>()` is failing, so I have skipped it for now.

Let's also discuss this on Discord.

I mainly glossed over the patch I haven't done a detailed review. I want to wait until the patch is no longer a draft.

@Zingam
Copy link
Contributor

Zingam commented Dec 28, 2023

@mordante Thank you for having a look at the patch.

@mordante I have three issues:

1. I disabled the deprecation warning or `visit_format_arg()` in the `visit.pass.cpp` file with `TEST_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-declarations")`. But many more tests are failing, even in tests (IMO) which are not related directly. What do I need to do?

Can you post links to the failed tests? Preferably on Discord, that makes the discussion easier.

(For the record) For example: https://github.com/llvm/llvm-project/actions/runs/7339918093/job/19985311407?pr=76449

2. The declaration of `visit` uses `deducing this`, which is available in Clang 18 only. As far I can tell the complication fails due to `clang-tidy 17` being used in the CI.

I recently ran into the same issue, we probably should suggest moving the the latest clang-tidy in the CI. I expect you need to disable some tests for older clang versions too and maybe in the code guard the code with the appropriate compiler FTM.

3. The test of the `handle` for `visit<R>()` is failing, so I have skipped it for now.

Let's also discuss this on Discord.

Sure

I mainly glossed over the patch I haven't done a detailed review. I want to wait until the patch is no longer a draft.

I believe that after resolving the above issues both PRs will be ready for a review.

@Zingam
Copy link
Contributor

Zingam commented Dec 30, 2023

@mordante I have three issues:

1. I disabled the deprecation warning or `visit_format_arg()` in the `visit.pass.cpp` file with `TEST_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-declarations")`. But many more tests are failing, even in tests (IMO) which are not related directly. What do I need to do?

Can you post links to the failed tests? Preferably on Discord, that makes the discussion easier.

2. The declaration of `visit` uses `deducing this`, which is available in Clang 18 only. As far I can tell the complication fails due to `clang-tidy 17` being used in the CI.

I recently ran into the same issue, we probably should suggest moving the the latest clang-tidy in the CI. I expect you need to disable some tests for older clang versions too and maybe in the code guard the code with the appropriate compiler FTM.

3. The test of the `handle` for `visit<R>()` is failing, so I have skipped it for now.

Let's also discuss this on Discord.

I mainly glossed over the patch I haven't done a detailed review. I want to wait until the patch is no longer a draft.

I believe I resolved issues 1 and 3 and only the clang-tidy 17 one remained to be resolved.

Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this.

@H-G-Hristov
Copy link
Contributor Author

Thanks for working on this.

Thank you for reviewing!

@ldionne ldionne marked this pull request as ready for review January 10, 2024 19:13
@H-G-Hristov
Copy link
Contributor Author

This FreeBSD failure seems unrelated, the same appears in variant:

# .---command stderr------------
# | In file included from /usr/home/buildkite/.buildkite-agent/builds/freebsd-test-1/llvm-project/libcxx-ci/libcxx/test/libcxx/assertions/modes/override_with_fast_mode.pass.cpp:19:
# | /home/buildkite/.buildkite-agent/builds/freebsd-test-1/llvm-project/libcxx-ci/libcxx/test/support/check_assertion.h:293:25: error: use of undeclared identifier 'SIGILL'
# |       if (exit_code_ == SIGILL || exit_code_ == SIGTRAP) {
# |                         ^
# | /home/buildkite/.buildkite-agent/builds/freebsd-test-1/llvm-project/libcxx-ci/libcxx/test/support/check_assertion.h:293:49: error: use of undeclared identifier 'SIGTRAP'
# |       if (exit_code_ == SIGILL || exit_code_ == SIGTRAP) {
# |                                                 ^
# | 2 errors generated.
# `-----------------------------
# error: command failed with exit status: 1

Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks LGTM modulo a few minor comment.

Comment on lines 1522 to 1526
// Clang-18 has support for deducing this, but it does not set the FTM.
#if defined(__cpp_explicit_this_parameter) || (defined(_LIBCPP_CLANG_VER ) &&_LIBCPP_CLANG_VER >= 1800)
# define _LIBCPP_HAS_EXPLICIT_THIS_PARAMETER
#endif

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make sure merging this patch does not duplicate this entry.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course!

@H-G-Hristov
Copy link
Contributor Author

@mordante Could you please have another look at the test/latest update.

Copy link
Member

@mordante mordante left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still LGTM!

@Zingam Zingam merged commit 7d9b5aa into llvm:main Jan 21, 2024
@H-G-Hristov H-G-Hristov deleted the hgh/libcxx/P2637R3-member-visit-format branch January 21, 2024 10:36
@petrhosek
Copy link
Member

The llvm-libc++-static-clangcl.cfg.in :: std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp is failing on Windows with the following error:

Exit Code: 1

Command Output (stdout):
--
# COMPILED WITH
C:/b/s/w/ir/x/w/llvm_build/./bin/clang-cl.exe C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp --driver-mode=g++ --target=x86_64-pc-windows-msvc -fms-runtime-lib=static -nostdinc++ -I C:/b/s/w/ir/x/w/llvm_build/include/c++/v1 -I C:/b/s/w/ir/x/w/llvm_build/include/x86_64-pc-windows-msvc/c++/v1 -I C:/b/s/w/ir/x/w/llvm-llvm-project/libcxx/test/support -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_STDIO_ISO_WIDE_SPECIFIERS -DNOMINMAX -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings  -llibc++experimental -nostdlib -L C:/b/s/w/ir/x/w/llvm_build/./lib/x86_64-pc-windows-msvc -llibc++ -llibcpmt -o C:\b\s\w\ir\x\w\llvm_build\runtimes\runtimes-x86_64-pc-windows-msvc-bins\test\std\utilities\format\format.arguments\format.arg\Output\visit.return_type.pass.cpp.dir\t.tmp.exe
# executed command: C:/b/s/w/ir/x/w/llvm_build/./bin/clang-cl.exe 'C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp' --driver-mode=g++ --target=x86_64-pc-windows-msvc -fms-runtime-lib=static -nostdinc++ -I C:/b/s/w/ir/x/w/llvm_build/include/c++/v1 -I C:/b/s/w/ir/x/w/llvm_build/include/x86_64-pc-windows-msvc/c++/v1 -I C:/b/s/w/ir/x/w/llvm-llvm-project/libcxx/test/support -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_STDIO_ISO_WIDE_SPECIFIERS -DNOMINMAX -std=c++26 -Werror -Wall -Wctad-maybe-unsupported -Wextra -Wshadow -Wundef -Wunused-template -Wno-unused-command-line-argument -Wno-attributes -Wno-pessimizing-move -Wno-noexcept-type -Wno-atomic-alignment -Wno-reserved-module-identifier -Wdeprecated-copy -Wdeprecated-copy-dtor -Wno-user-defined-literals -Wno-tautological-compare -Wsign-compare -Wunused-variable -Wunused-parameter -Wunreachable-code -Wno-unused-local-typedef -Wno-local-type-template-args -Wno-c++11-extensions -Wno-unknown-pragmas -Wno-pass-failed -Wno-mismatched-new-delete -Wno-redundant-move -Wno-self-move -D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER -D_LIBCPP_ENABLE_EXPERIMENTAL -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE -Werror=thread-safety -Wuser-defined-warnings -llibc++experimental -nostdlib -L C:/b/s/w/ir/x/w/llvm_build/./lib/x86_64-pc-windows-msvc -llibc++ -llibcpmt -o 'C:\b\s\w\ir\x\w\llvm_build\runtimes\runtimes-x86_64-pc-windows-msvc-bins\test\std\utilities\format\format.arguments\format.arg\Output\visit.return_type.pass.cpp.dir\t.tmp.exe'
# .---command stderr------------
# | C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:134:35: error: implicit conversion from 'long long' to 'const long' changes value from 192812079084 to -461449236 [-Werror,-Wconstant-conversion]
# |   134 |   test<Context, bool, long>(true, 192812079084L);
# |       |   ~~~~                            ^~~~~~~~~~~~~
# | C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:363:3: note: in instantiation of function template specialization 'test<char>' requested here
# |   363 |   test<char>();
# |       |   ^
# | C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:135:36: error: implicit conversion from 'long long' to 'const long' changes value from 192812079084 to -461449236 [-Werror,-Wconstant-conversion]
# |   135 |   test<Context, bool, long>(false, 192812079084L);
# |       |   ~~~~                             ^~~~~~~~~~~~~
# | C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:40:12: error: implicit conversion from 'long long' to 'long' changes value from 192812079084 to -461449236 [-Werror,-Wconstant-conversion]
# |    40 |     return 192812079084L;
# |       |     ~~~~~~ ^~~~~~~~~~~~~
# | C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:60:20: note: in instantiation of function template specialization 'make_expected_result<long>' requested here
# |    60 |             return make_expected_result<ExpectedR>();
# |       |                    ^
# | C:/b/s/w/ir/x/w/llvm_build/include/c++/v1\__type_traits/invoke.h:344:25: note: in instantiation of function template specialization 'test(bool, const long &)::(anonymous class)::operator()<bool>' requested here
# |   344 |                { return static_cast<_Fp&&>(__f)(static_cast<_Args&&>(__args)...); }
# |       |                         ^
# | C:/b/s/w/ir/x/w/llvm_build/include/c++/v1\__functional/invoke.h:28:15: note: in instantiation of function template specialization 'std::__invoke<(lambda at C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:57:54), bool &>' requested here
# |    28 |   return std::__invoke(std::forward<_Fn>(__f), std::forward<_Args>(__args)...);
# |       |               ^
# | C:/b/s/w/ir/x/w/llvm_build/include/c++/v1\__functional/invoke.h:47:17: note: in instantiation of function template specialization 'std::invoke<(lambda at C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:57:54), bool &>' requested here
# |    47 |     return std::invoke(std::forward<_Fn>(__f), std::forward<_Args>(__args)...);
# |       |                 ^
# | C:/b/s/w/ir/x/w/llvm_build/include/c++/v1\__format/format_arg.h:155:17: note: in instantiation of function template specialization 'std::invoke_r<long, (lambda at C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:57:54), bool &>' requested here
# |   155 |     return std::invoke_r<_Rp>(std::forward<_Visitor>(__vis), __arg.__value_.__boolean_);
# |       |                 ^
# | C:/b/s/w/ir/x/w/llvm_build/include/c++/v1\__format/format_arg.h:323:19: note: in instantiation of function template specialization 'std::__visit_format_arg<long, (lambda at C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:57:54), std::basic_format_context<char *, char>>' requested here
# |   323 |       return std::__visit_format_arg<_Rp>(std::forward<_Visitor>(__vis), __arg);
# |       |                   ^
# | C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:57:37: note: in instantiation of function template specialization 'std::basic_format_arg<std::basic_format_context<char *, char>>::visit<long, (lambda at C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:57:54)>' requested here
# |    57 |         format_args.get(0).template visit<ExpectedR>([v = To(value)](auto a) -> ExpectedR {
# |       |                                     ^
# | C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:134:3: note: in instantiation of function template specialization 'test<std::basic_format_context<char *, char>, bool, long, bool>' requested here
# |   134 |   test<Context, bool, long>(true, 192812079084L);
# |       |   ^
# | C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:363:3: note: in instantiation of function template specialization 'test<char>' requested here
# |   363 |   test<char>();
# |       |   ^
# | C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:134:35: error: implicit conversion from 'long long' to 'const long' changes value from 192812079084 to -461449236 [-Werror,-Wconstant-conversion]
# |   134 |   test<Context, bool, long>(true, 192812079084L);
# |       |   ~~~~                            ^~~~~~~~~~~~~
# | C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:365:3: note: in instantiation of function template specialization 'test<wchar_t>' requested here
# |   365 |   test<wchar_t>();
# |       |   ^
# | C:\b\s\w\ir\x\w\llvm-llvm-project\libcxx\test\std\utilities\format\format.arguments\format.arg\visit.return_type.pass.cpp:135:36: error: implicit conversion from 'long long' to 'const long' changes value from 192812079084 to -461449236 [-Werror,-Wconstant-conversion]
# |   135 |   test<Context, bool, long>(false, 192812079084L);
# |       |   ~~~~                             ^~~~~~~~~~~~~
# | 5 errors generated.
# `-----------------------------
# error: command failed with exit status: 1

--

Would it be possible to take a look and revert the change if you cannot do a quick fix forward?

petrhosek added a commit that referenced this pull request Jan 22, 2024
Use correct CMake variable.
@petrhosek
Copy link
Member

Please ignore 04c8558 which addresses a different issue, this is still broken in tip-of-tree.

@petrhosek
Copy link
Member

This is still broken so I'm going to revert the change.

petrhosek added a commit that referenced this pull request Jan 22, 2024
…arg`) (#76449)"

This reverts commit 7d9b5aa since
std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp
is failing on Windows when building with Clang-cl.
H-G-Hristov pushed a commit to H-G-Hristov/llvm-project that referenced this pull request Jan 22, 2024
@H-G-Hristov
Copy link
Contributor Author

This is still broken so I'm going to revert the change.

Thanks! Sorry for the inconvenience. I didn't see your message earlier.

Zingam added a commit that referenced this pull request Jan 29, 2024
…#76449 (#79032)

Deleted the offending test case.


`libcxx/test/std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp`
lines: 134-135:   
>   test<Context, bool, long>(true, 192812079084L);
     test<Context, bool, long>(false, 192812079084L);
     
 Relands: #76449
Reverted in:
02f95b7

---------

Co-authored-by: Zingam <[email protected]>
blueboxd pushed a commit to blueboxd/libcxx that referenced this pull request Feb 7, 2024
…) #76449 (#79032)

Deleted the offending test case.

`libcxx/test/std/utilities/format/format.arguments/format.arg/visit.return_type.pass.cpp`
lines: 134-135:
>   test<Context, bool, long>(true, 192812079084L);
     test<Context, bool, long>(false, 192812079084L);

 Relands: llvm/llvm-project#76449
Reverted in:
llvm/llvm-project@02f95b7

---------

Co-authored-by: Zingam <[email protected]>
NOKEYCHECK=True
GitOrigin-RevId: 27e67cdb31bed4c346af925686c031d57aef9846
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants