diff --git a/stl/inc/format b/stl/inc/format index 16466b6980d..f968858c563 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -305,7 +305,8 @@ constexpr const _CharT* _Parse_format_specs(const _CharT* _Begin, const _CharT* template struct formatter; -// TODO: test coverage +inline void _You_see_this_error_because_arg_id_is_out_of_range() noexcept {} + template class basic_format_parse_context { public: @@ -313,15 +314,6 @@ public: using const_iterator = typename basic_string_view<_CharT>::const_iterator; using iterator = const_iterator; -private: - basic_string_view<_CharT> _Format_string; - size_t _Num_args; - // The standard says this is size_t, however we use ptrdiff_t to save some space - // by not having to store the indexing mode. Below is a more detailed explanation - // of how this works. - ptrdiff_t _Next_arg_id = 0; - -public: constexpr explicit basic_format_parse_context(basic_string_view<_CharT> _Fmt, size_t _Num_args_ = 0) noexcept : _Format_string(_Fmt), _Num_args(_Num_args_) {} basic_format_parse_context(const basic_format_parse_context&) = delete; @@ -333,8 +325,10 @@ public: _NODISCARD constexpr const_iterator end() const noexcept { return _Format_string.end(); } - constexpr void advance_to(const_iterator _It) { - _Format_string.remove_prefix(_It - begin()); + constexpr void advance_to(const const_iterator _It) { + _Adl_verify_range(_It, _Format_string.end()); + const auto _Diff = static_cast(_It._Unwrapped() - _Format_string._Unchecked_begin()); + _Format_string.remove_prefix(_Diff); } // While the standard presents an exposition only enum value for @@ -343,18 +337,33 @@ public: // _Next_arg_id > 0 means automatic // _Next_arg_id == -1 means manual constexpr size_t next_arg_id() { - if (_Next_arg_id >= 0) { - return _Next_arg_id++; + if (_Next_arg_id == -1) { + throw format_error("Can not switch from manual to automatic indexing"); } - throw format_error("Can not switch from manual to automatic indexing"); + + return static_cast(_Next_arg_id++); } - constexpr void check_arg_id(size_t _Id) { - (void) _Id; + + constexpr void check_arg_id(const size_t _Id) { + if (_STD is_constant_evaluated()) { + if (_Id >= _Num_args) { + _You_see_this_error_because_arg_id_is_out_of_range(); + } + } + if (_Next_arg_id > 0) { throw format_error("Can not switch from automatic to manual indexing"); } _Next_arg_id = -1; } + +private: + basic_string_view<_CharT> _Format_string; + size_t _Num_args; + // The standard says this is size_t, however we use ptrdiff_t to save some space + // by not having to store the indexing mode. Above is a more detailed explanation + // of how this works. + ptrdiff_t _Next_arg_id = 0; }; // TODO: test coverage diff --git a/tests/std/test.lst b/tests/std/test.lst index de1a48a5e19..7d5f3a8123d 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -243,6 +243,7 @@ tests\P0595R2_is_constant_evaluated tests\P0607R0_inline_variables tests\P0616R0_using_move_in_numeric tests\P0631R8_numbers_math_constants +tests\P0645R10_text_formatting_death tests\P0645R10_text_formatting_parse_contexts tests\P0645R10_text_formatting_parsing tests\P0660R10_jthread_and_cv_any diff --git a/tests/std/tests/P0645R10_text_formatting_death/env.lst b/tests/std/tests/P0645R10_text_formatting_death/env.lst new file mode 100644 index 00000000000..22f1f0230a4 --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_winsdk_concepts_matrix.lst diff --git a/tests/std/tests/P0645R10_text_formatting_death/test.cpp b/tests/std/tests/P0645R10_text_formatting_death/test.cpp new file mode 100644 index 00000000000..86e74da6037 --- /dev/null +++ b/tests/std/tests/P0645R10_text_formatting_death/test.cpp @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include +#include + +#include +using namespace std; + +void test_case_advance_no_range() { + const auto format_string = "First {} and second {} and third {}"sv; + const auto other_format_string = "something"sv; + basic_format_parse_context context{format_string}; + context.advance_to(other_format_string.begin()); +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec; + +#if _ITERATOR_DEBUG_LEVEL != 0 + exec.add_death_tests({ + test_case_advance_no_range, + }); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + return exec.run(argc, argv); +} diff --git a/tests/std/tests/P0645R10_text_formatting_parse_contexts/env.lst b/tests/std/tests/P0645R10_text_formatting_parse_contexts/env.lst index f3ccc8613c6..22f1f0230a4 100644 --- a/tests/std/tests/P0645R10_text_formatting_parse_contexts/env.lst +++ b/tests/std/tests/P0645R10_text_formatting_parse_contexts/env.lst @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\concepts_matrix.lst +RUNALL_INCLUDE ..\strict_winsdk_concepts_matrix.lst diff --git a/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp b/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp index 7a3f5c0dad2..606f08b51f4 100644 --- a/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_parse_contexts/test.cpp @@ -2,11 +2,108 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include +#include +#include #include +#include #include +#include -// TODO: fill in tests +using namespace std; + +template +constexpr auto get_input() { + if constexpr (same_as) { + return "First {} and second {} and third {}"sv; + } else { + return L"First {} and second {} and third {}"sv; + } +} + +template +constexpr bool ensure_is_constant_expression(const bool should_be_constant_expression) { + basic_format_parse_context context{get_input(), 1}; + if (should_be_constant_expression) { + context.check_arg_id(0); + } else { + context.check_arg_id(1); + } + + return is_constant_evaluated(); +} + +const bool check_arg_id_is_constexpr_char = ensure_is_constant_expression(true); +const bool check_arg_id_not_constexpr_char = !ensure_is_constant_expression(false); +const bool check_arg_id_is_constexpr_wchar = ensure_is_constant_expression(true); +const bool check_arg_id_not_constexpr_wchar = !ensure_is_constant_expression(false); + +template +constexpr bool test_basic_format_parse_context() { + static_assert(!is_copy_constructible_v>); + static_assert(!is_copy_assignable_v>); + + const auto format_string = get_input(); + { // iterator interface + basic_format_parse_context context{format_string}; + const same_as::const_iterator> auto b = context.begin(); + assert(b == format_string.begin()); + static_assert(noexcept(context.begin())); + + const same_as::const_iterator> auto e = context.end(); + assert(e == format_string.end()); + static_assert(noexcept(context.end())); + + const auto new_position = format_string.begin() + 5; + context.advance_to(new_position); + assert(to_address(context.begin()) == to_address(new_position)); + assert(to_address(context.end()) == to_address(e)); + } + + { // arg_id + basic_format_parse_context context{format_string}; + const same_as auto first_arg_id = context.next_arg_id(); + assert(first_arg_id == 0); + + const same_as auto second_arg_id = context.next_arg_id(); + assert(second_arg_id == 1); + + if (!is_constant_evaluated()) { + try { + context.check_arg_id(0); + } catch (const format_error& e) { + assert(e.what() == "Can not switch from automatic to manual indexing"sv); + } + } + } + + { // check_arg_id + basic_format_parse_context context{format_string, 3}; + context.check_arg_id(0); + context.check_arg_id(1); + context.check_arg_id(2); // intentional duplicates to check whether this is ok to call multiple times + context.check_arg_id(1); + context.check_arg_id(0); + + if (!is_constant_evaluated()) { + try { + context.next_arg_id(); + } catch (const format_error& e) { + assert(e.what() == "Can not switch from manual to automatic indexing"sv); + } + } + } + + return true; +} int main() { - return 0; + test_basic_format_parse_context(); + test_basic_format_parse_context(); + static_assert(test_basic_format_parse_context()); + static_assert(test_basic_format_parse_context()); + + assert(check_arg_id_is_constexpr_char); + assert(check_arg_id_not_constexpr_char); + assert(check_arg_id_is_constexpr_wchar); + assert(check_arg_id_not_constexpr_wchar); }