From a6c43e8ca8f622ee2da4bad5458ebf5cecf6dd90 Mon Sep 17 00:00:00 2001 From: Mark de Wever Date: Sun, 22 Jan 2023 13:31:27 +0100 Subject: [PATCH] [libc++][format] Fixes usage of contiguous ranges. The contiguous range made incorrect assumptions for certain input ranges. Fixes llvm.org/PR60164 Reviewed By: #libc, ldionne Differential Revision: https://reviews.llvm.org/D142302 --- libcxx/include/__format/range_formatter.h | 11 +++- .../format.functions.tests.h | 63 ++++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/libcxx/include/__format/range_formatter.h b/libcxx/include/__format/range_formatter.h index 6188842..9ea61a7 100644 --- a/libcxx/include/__format/range_formatter.h +++ b/libcxx/include/__format/range_formatter.h @@ -29,6 +29,8 @@ #include <__format/parser_std_format_spec.h> #include <__iterator/back_insert_iterator.h> #include <__ranges/concepts.h> +#include <__ranges/data.h> +#include <__ranges/size.h> #include <__type_traits/remove_cvref.h> #include @@ -165,11 +167,16 @@ struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT range_formatter { // When the range is contiguous use a basic_string_view instead to avoid a // copy of the underlying data. The basic_string_view formatter // specialization is the "basic" string formatter in libc++. - if constexpr (ranges::contiguous_range<_Rp>) { + if constexpr (ranges::contiguous_range<_Rp> && std::ranges::sized_range<_Rp>) { std::formatter, _CharT> __formatter; if (__debug_format) __formatter.set_debug_format(); - return __formatter.format(basic_string_view<_CharT>{__range.data(), __range.size()}, __ctx); + return __formatter.format( + basic_string_view<_CharT>{ + ranges::data(__range), + ranges::size(__range), + }, + __ctx); } else { std::formatter, _CharT> __formatter; if (__debug_format) diff --git a/libcxx/test/std/utilities/format/format.range/format.range.formatter/format.functions.tests.h b/libcxx/test/std/utilities/format/format.range/format.range.formatter/format.functions.tests.h index 890f597..a465916 100644 --- a/libcxx/test/std/utilities/format/format.range/format.range.formatter/format.functions.tests.h +++ b/libcxx/test/std/utilities/format/format.range/format.range.formatter/format.functions.tests.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1262,6 +1263,66 @@ void test_with_ranges(TestFunction check, ExceptionTest check_exception) { } // +// Adaptor +// + +template +class non_contiguous { + // A deque iterator is random access, but not contiguous. + using adaptee = std::deque; + +public: + using iterator = typename adaptee::iterator; + using pointer = typename adaptee::pointer; + + iterator begin() { return data_.begin(); } + iterator end() { return data_.end(); } + + explicit non_contiguous(adaptee&& data) : data_(std::move(data)) {} + +private: + adaptee data_; +}; + +template +class contiguous { + // A vector iterator is contiguous. + using adaptee = std::vector; + +public: + using iterator = typename adaptee::iterator; + using pointer = typename adaptee::pointer; + + iterator begin() { return data_.begin(); } + iterator end() { return data_.end(); } + + explicit contiguous(adaptee&& data) : data_(std::move(data)) {} + +private: + adaptee data_; +}; + +// This tests two different implementations in libc++. A basic_string_view +// formatter if the range is contiguous, a basic_string otherwise. +template +void test_adaptor(TestFunction check, ExceptionTest check_exception) { + static_assert(std::format_kind> == std::range_format::sequence); + static_assert(std::ranges::sized_range>); + static_assert(!std::ranges::contiguous_range>); + test_char_string( + check, + check_exception, + non_contiguous{std::deque{CharT('H'), CharT('e'), CharT('l'), CharT('l'), CharT('o')}}); + + static_assert(std::format_kind> == std::range_format::sequence); + static_assert(std::ranges::sized_range>); + static_assert(std::ranges::contiguous_range>); + test_char_string(check, + check_exception, + contiguous{std::vector{CharT('H'), CharT('e'), CharT('l'), CharT('l'), CharT('o')}}); +} + +// // Driver // @@ -1285,6 +1346,8 @@ void format_tests(TestFunction check, ExceptionTest check_exception) { test_tuple_int_int_int(check, check_exception); test_with_ranges(check, check_exception); + + test_adaptor(check, check_exception); } #endif // TEST_STD_UTILITIES_FORMAT_FORMAT_RANGE_FORMAT_RANGE_FORMATTER_FORMAT_FUNCTIONS_TESTS_H -- 2.7.4