diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv --- a/libcxx/docs/Status/Cxx2bIssues.csv +++ b/libcxx/docs/Status/Cxx2bIssues.csv @@ -83,7 +83,7 @@ `3532 `__,"``split_view::inner-iterator::operator++(int)`` should depend on ``Base``","June 2021","","","|ranges|" `3533 `__,"Make ``base() const &`` consistent across iterator wrappers that supports ``input_iterators``","June 2021","|Complete|","14.0","|ranges|" `3536 `__,"Should ``chrono::from_stream()`` assign zero to duration for failure?","June 2021","","","|chrono|" -`3539 `__,"``format_to`` must not copy models of ``output_iterator``","June 2021","","","|format|" +`3539 `__,"``format_to`` must not copy models of ``output_iterator``","June 2021","|Complete|","14.0","|format|" `3540 `__,"ยง[format.arg] There should be no const in ``basic_format_arg(const T* p)``","June 2021","|Complete|","14.0","|format|" `3541 `__,"``indirectly_readable_traits`` should be SFINAE-friendly for all types","June 2021","|Complete|","14.0","|ranges|" `3542 `__,"``basic_format_arg`` mishandles ``basic_string_view`` with custom traits","June 2021","|Complete|","14.0","|format|" @@ -120,7 +120,7 @@ `3561 `__,"Issue with internal counter in ``discard_block_engine``","October 2021","","" `3563 `__,"``keys_view`` example is broken","October 2021","","","|ranges|" `3566 `__,"Constraint recursion for ``operator<=>(optional, U)``","October 2021","","","|spaceship|" -`3567 `__,"Formatting move-only iterators take two","October 2021","","","|format|" +`3567 `__,"Formatting move-only iterators take two","October 2021","|Complete|","16.0","|format|" `3568 `__,"``basic_istream_view`` needs to initialize ``value_``","October 2021","","","|ranges|" `3570 `__,"``basic_osyncstream::emit`` should be an unformatted output function","October 2021","","" `3571 `__,"``flush_emit`` should set ``badbit`` if the ``emit`` call fails","October 2021","","" diff --git a/libcxx/include/__format/buffer.h b/libcxx/include/__format/buffer.h --- a/libcxx/include/__format/buffer.h +++ b/libcxx/include/__format/buffer.h @@ -11,6 +11,7 @@ #define _LIBCPP___FORMAT_BUFFER_H #include <__algorithm/copy_n.h> +#include <__algorithm/ranges_copy_n.h> #include <__algorithm/fill_n.h> #include <__algorithm/max.h> #include <__algorithm/min.h> @@ -275,10 +276,10 @@ _LIBCPP_HIDE_FROM_ABI explicit __writer_iterator(_OutIt __out_it) : __out_it_{_VSTD::move(__out_it)} {} - _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() { return __out_it_; } + _LIBCPP_HIDE_FROM_ABI _OutIt __out_it() && { return _VSTD::move(__out_it_); } _LIBCPP_HIDE_FROM_ABI void __flush(_CharT* __ptr, size_t __n) { - __out_it_ = _VSTD::copy_n(__ptr, __n, _VSTD::move(__out_it_)); + __out_it_ = _VSTD::ranges::copy_n(__ptr, __n, _VSTD::move(__out_it_)).out; } private: diff --git a/libcxx/include/__format/format_context.h b/libcxx/include/__format/format_context.h --- a/libcxx/include/__format/format_context.h +++ b/libcxx/include/__format/format_context.h @@ -101,8 +101,8 @@ return *__loc_; } #endif - _LIBCPP_HIDE_FROM_ABI iterator out() { return __out_it_; } - _LIBCPP_HIDE_FROM_ABI void advance_to(iterator __it) { __out_it_ = __it; } + _LIBCPP_HIDE_FROM_ABI iterator out() { return _VSTD::move(__out_it_); } + _LIBCPP_HIDE_FROM_ABI void advance_to(iterator __it) { __out_it_ = _VSTD::move(__it); } private: iterator __out_it_; diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/advance_to.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/advance_to.pass.cpp --- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/advance_to.pass.cpp +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/advance_to.pass.cpp @@ -18,6 +18,7 @@ #include "test_macros.h" #include "test_format_context.h" +#include "test_format_iterator.h" template void test( @@ -43,27 +44,25 @@ void test() { test(std::basic_format_args( - std::make_format_args>, char>>())); + std::make_format_args< + std::basic_format_context>, char>>())); #ifndef TEST_HAS_NO_WIDE_CHARACTERS test(std::basic_format_args( - std::make_format_args>, wchar_t>>())); + std::make_format_args< + std::basic_format_context>, wchar_t>>())); #endif #ifndef TEST_HAS_NO_CHAR8_T test(std::basic_format_args( - std::make_format_args>, char8_t>>())); + std::make_format_args< + std::basic_format_context>, char8_t>>())); #endif test(std::basic_format_args( - std::make_format_args>, - char16_t>>())); + std::make_format_args< + std::basic_format_context>, char16_t>>())); test(std::basic_format_args( - std::make_format_args>, - char32_t>>())); + std::make_format_args< + std::basic_format_context>, char32_t>>())); } int main(int, char**) { diff --git a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/out.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/out.pass.cpp --- a/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/out.pass.cpp +++ b/libcxx/test/std/utilities/format/format.formatter/format.context/format.context/out.pass.cpp @@ -18,18 +18,20 @@ #include "test_macros.h" #include "test_format_context.h" +#include "test_format_iterator.h" template void test( std::basic_format_args> args) { { std::basic_string str; - OutIt out_it{str}; - std::basic_format_context context = - test_format_context_create(out_it, args); - context.out() = CharT('a'); - context.out() = CharT('b'); - context.out() = CharT('c'); + std::basic_format_context context = test_format_context_create(OutIt{str}, args); + + // Note this operation is moving the iterator + OutIt out_it = context.out(); + out_it = CharT('a'); + out_it = CharT('b'); + out_it = CharT('c'); assert(str.size() == 3); assert(str[0] == CharT('a')); @@ -40,26 +42,24 @@ void test() { test(std::basic_format_args( - std::make_format_args>, char>>())); + std::make_format_args< + std::basic_format_context>, char>>())); #ifndef TEST_HAS_NO_WIDE_CHARACTERS test(std::basic_format_args( - std::make_format_args>, wchar_t>>())); + std::make_format_args< + std::basic_format_context>, wchar_t>>())); #endif #ifndef TEST_HAS_NO_CHAR8_T test(std::basic_format_args( - std::make_format_args>, char8_t>>())); + std::make_format_args< + std::basic_format_context>, char8_t>>())); #endif test(std::basic_format_args( - std::make_format_args>, - char16_t>>())); + std::make_format_args< + std::basic_format_context>, char16_t>>())); test(std::basic_format_args( - std::make_format_args>, - char32_t>>())); + std::make_format_args< + std::basic_format_context>, char32_t>>())); } int main(int, char**) { diff --git a/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp --- a/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/format_to.locale.pass.cpp @@ -28,9 +28,10 @@ #include #include -#include "test_macros.h" #include "format_tests.h" #include "string_literal.h" +#include "test_format_iterator.h" +#include "test_macros.h" auto test = [](std::basic_string_view expected, const Args&... args) constexpr { @@ -42,7 +43,7 @@ } { std::list out; - std::format_to(std::back_inserter(out), std::locale(), fmt.template sv(), args...); + std::format_to(move_only_push_back_iterator(out), std::locale(), fmt.template sv(), args...); assert(std::equal(out.begin(), out.end(), expected.begin(), expected.end())); } { diff --git a/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp b/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp --- a/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp +++ b/libcxx/test/std/utilities/format/format.functions/format_to.pass.cpp @@ -28,6 +28,7 @@ #include "test_macros.h" #include "format_tests.h" #include "string_literal.h" +#include "test_format_iterator.h" auto test = [](std::basic_string_view expected, const Args&... args) constexpr { @@ -46,6 +47,7 @@ std::vector out; std::format_to(std::back_inserter(out), fmt.template sv(), args...); assert(std::equal(out.begin(), out.end(), expected.begin(), expected.end())); + assert(std::equal(out.begin(), out.end(), expected.begin(), expected.end())); } { assert(expected.size() < 4096 && "Update the size of the buffer."); diff --git a/libcxx/test/support/test_format_iterator.h b/libcxx/test/support/test_format_iterator.h new file mode 100644 --- /dev/null +++ b/libcxx/test/support/test_format_iterator.h @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef SUPPORT_TEST_FORMAT_ITERATOR_H +#define SUPPORT_TEST_FORMAT_ITERATOR_H + +#include +#include + +#include "test_macros.h" + +#if TEST_STD_VER > 17 + +template +class move_only_push_back_iterator { +public: + // No Legacy tag since the code isn't available prior to C++20. + using value_type = void; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = void; + using char_type = typename C::value_type; + + constexpr explicit move_only_push_back_iterator(C& container) : container_(std::addressof(container)) {} + + move_only_push_back_iterator(const move_only_push_back_iterator&) = delete; + move_only_push_back_iterator(move_only_push_back_iterator&& other) : container_(other.container_) { + assert(other.container_); + other.container_ = nullptr; + } + + move_only_push_back_iterator& operator=(const move_only_push_back_iterator&) = delete; + move_only_push_back_iterator& operator=(move_only_push_back_iterator&& other) { + assert(std::addressof(other) != this); + assert(other.container_); + container_ = other.container_; + other.container_ = nullptr; + + return *this; + } + + _LIBCPP_HIDE_FROM_ABI move_only_push_back_iterator& operator=(char_type c) { + assert(container_); + container_->push_back(c); + return *this; + } + constexpr move_only_push_back_iterator& operator*() { return *this; } + constexpr move_only_push_back_iterator& operator++() { return *this; } + constexpr move_only_push_back_iterator& operator++(int) { return *this; } + +private: + C* container_; +}; + +#endif // TEST_STD_VER > 17 + +#endif // SUPPORT_TEST_FORMAT_ITERATOR_H