diff --git a/libcxx/include/__config b/libcxx/include/__config --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -536,6 +536,10 @@ #define _LIBCPP_HAS_NO_VARIABLE_TEMPLATES #endif +#if !defined(__cpp_concepts) || __cpp_concepts < 201907L +#define _LIBCPP_HAS_NO_CONCEPTS +#endif + #if !defined(_LIBCPP_HAS_NO_ASAN) && !defined(__SANITIZE_ADDRESS__) #define _LIBCPP_HAS_NO_ASAN #endif diff --git a/libcxx/include/format b/libcxx/include/format --- a/libcxx/include/format +++ b/libcxx/include/format @@ -19,14 +19,51 @@ explicit format_error(const string& what_arg); explicit format_error(const char* what_arg); }; + + // [format.parse.ctx], class template basic_format_parse_context + template + class basic_format_parse_context { + public: + using char_type = charT; + using const_iterator = typename basic_string_view::const_iterator; + using iterator = const_iterator; + + private: + iterator begin_; // exposition only + iterator end_; // exposition only + enum indexing { unknown, manual, automatic }; // exposition only + indexing indexing_; // exposition only + size_t next_arg_id_; // exposition only + size_t num_args_; // exposition only + + public: + constexpr explicit basic_format_parse_context(basic_string_view fmt, + size_t num_args = 0) noexcept; + basic_format_parse_context(const basic_format_parse_context&) = delete; + basic_format_parse_context& operator=(const basic_format_parse_context&) = delete; + + constexpr const_iterator begin() const noexcept; + constexpr const_iterator end() const noexcept; + constexpr void advance_to(const_iterator it); + + constexpr size_t next_arg_id(); + constexpr void check_arg_id(size_t id); + }; + using format_parse_context = basic_format_parse_context; + using wformat_parse_context = basic_format_parse_context; } */ #include <__config> #include +#include #include +#ifdef _LIBCPP_NO_EXCEPTIONS +#include +#endif + #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header #endif @@ -38,6 +75,12 @@ #if _LIBCPP_STD_VER > 17 +// TODO FMT Remove this once we require compilers with proper C++20 support. +// If the compiler has no concepts support, the format header will be disabled. +// Without concepts support enable_if needs to be used and that too much effort +// to support compilers with partial C++20 support. +#ifndef _LIBCPP_HAS_NO_CONCEPTS + class _LIBCPP_EXCEPTION_ABI format_error : public runtime_error { public: _LIBCPP_INLINE_VISIBILITY explicit format_error(const string& __s) @@ -47,6 +90,89 @@ virtual ~format_error() noexcept; }; +_LIBCPP_NORETURN inline _LIBCPP_INLINE_VISIBILITY void +__throw_format_error(const char* __s) { +#ifndef _LIBCPP_NO_EXCEPTIONS + throw format_error(__s); +#else + (void)__s; + _VSTD::abort(); +#endif +} + +template +class _LIBCPP_TEMPLATE_VIS basic_format_parse_context { +public: + using char_type = _CharT; + using const_iterator = + typename _VSTD::basic_string_view<_CharT>::const_iterator; + using iterator = const_iterator; + +public: + _LIBCPP_INLINE_VISIBILITY + constexpr explicit basic_format_parse_context( + _VSTD::basic_string_view<_CharT> __fmt, size_t __num_args = 0) noexcept + : __begin_(__fmt.begin()), + __end_(__fmt.end()), + __indexing_(__unknown), + __next_arg_id_(0), + __num_args_(__num_args) {} + + basic_format_parse_context(const basic_format_parse_context&) = delete; + basic_format_parse_context& + operator=(const basic_format_parse_context&) = delete; + + _LIBCPP_INLINE_VISIBILITY constexpr const_iterator begin() const noexcept { + return __begin_; + } + _LIBCPP_INLINE_VISIBILITY constexpr const_iterator end() const noexcept { + return __end_; + } + _LIBCPP_INLINE_VISIBILITY constexpr void advance_to(const_iterator __it) { + __begin_ = __it; + } + + _LIBCPP_INLINE_VISIBILITY constexpr size_t next_arg_id() { + if (__indexing_ == __manual) + __throw_format_error("Using automatic argument numbering in manual " + "argument numbering mode"); + + if (__indexing_ == __unknown) + __indexing_ = __automatic; + return __next_arg_id_++; + } + _LIBCPP_INLINE_VISIBILITY constexpr void check_arg_id(size_t __id) { + if (__indexing_ == __automatic) + __throw_format_error("Using manual argument numbering in automatic " + "argument numbering mode"); + + if (__indexing_ == __unknown) + __indexing_ = __manual; + + // Throws an exception to make the expression a non core constant + // expression as required by: + // [format.parse.ctx]/11 + // Remarks: Call expressions where id >= num_args_ are not core constant + // expressions ([expr.const]). + // Note: the Throws clause [format.parse.ctx]/10 doesn't specify the + // behavior when id >= num_args_. + if (_VSTD::is_constant_evaluated() && __id >= __num_args_) + __throw_format_error("Argument index outside the valid range"); + } + +private: + iterator __begin_; + iterator __end_; + enum _Indexing { __unknown, __manual, __automatic }; + _Indexing __indexing_; + size_t __next_arg_id_; + size_t __num_args_; +}; + +using format_parse_context = basic_format_parse_context; +using wformat_parse_context = basic_format_parse_context; + +#endif //_LIBCPP_HAS_NO_CONCEPTS #endif //_LIBCPP_STD_VER > 17 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/advance_to.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/advance_to.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/advance_to.pass.cpp @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// constexpr void advance_to(const_iterator it); + +#include +#include + +#include "test_macros.h" + +template +constexpr void test(const CharT* fmt) { + { + std::basic_format_parse_context context(fmt); + + context.advance_to(context.begin() + 1); + assert(context.begin() == &fmt[1]); + + context.advance_to(context.begin() + 1); + assert(context.begin() == &fmt[2]); + + context.advance_to(context.begin() + 1); + assert(context.begin() == context.end()); + } + { + std::basic_string_view view{fmt}; + std::basic_format_parse_context context(view); + + context.advance_to(context.begin() + 1); + assert(context.begin() == view.begin() + 1); + + context.advance_to(context.begin() + 1); + assert(context.begin() == view.begin() + 2); + + context.advance_to(context.begin() + 1); + assert(context.begin() == context.end()); + } +} + +constexpr bool test() { + test("abc"); + test(L"abc"); +#ifndef _LIBCPP_NO_HAS_CHAR8_T + test(u8"abc"); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(u"abc"); + test(U"abc"); +#endif + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/begin.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/begin.pass.cpp @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// constexpr begin() const noexcept; + +#include +#include + +#include "test_macros.h" + +template +constexpr void test(const CharT* fmt) { + { + std::basic_format_parse_context context(fmt); + assert(context.begin() == &fmt[0]); + ASSERT_NOEXCEPT(context.begin()); + } + { + std::basic_string_view view{fmt}; + std::basic_format_parse_context context(view); + assert(context.begin() == view.begin()); + ASSERT_NOEXCEPT(context.begin()); + } +} + +constexpr bool test() { + test("abc"); + test(L"abc"); +#ifndef _LIBCPP_NO_HAS_CHAR8_T + test(u8"abc"); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(u"abc"); + test(U"abc"); +#endif + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/check_arg_id.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/check_arg_id.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/check_arg_id.pass.cpp @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// 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: no-exceptions + +// This test requires the dylib support introduced in D92214. +// XFAIL: with_system_cxx_lib=macosx10.15 +// XFAIL: with_system_cxx_lib=macosx10.14 +// XFAIL: with_system_cxx_lib=macosx10.13 +// XFAIL: with_system_cxx_lib=macosx10.12 +// XFAIL: with_system_cxx_lib=macosx10.11 +// XFAIL: with_system_cxx_lib=macosx10.10 +// XFAIL: with_system_cxx_lib=macosx10.9 + +// + +// constexpr void check_arg_id(size_t id); + +#include +#include + +#include "test_macros.h" + +constexpr bool test() { + std::format_parse_context context("", 10); + for (size_t i = 0; i < 10; ++i) + context.check_arg_id(i); + + return true; +} + +void test_exception() { + [] { + std::format_parse_context context("", 1); + context.next_arg_id(); + try { + context.check_arg_id(0); + assert(false); + } catch (const std::format_error& e) { + assert(strcmp(e.what(), "Using manual argument numbering in automatic " + "argument numbering mode") == 0); + return; + } + assert(false); + }(); + + auto test_arg = [](size_t num_args) { + std::format_parse_context context("", num_args); + // Out of bounds access is valid if !std::is_constant_evaluated() + for (size_t i = 0; i <= num_args; ++i) + context.check_arg_id(i); + }; + for (size_t i = 0; i < 10; ++i) + test_arg(i); +} + +int main(int, char**) { + test(); + test_exception(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/ctor.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/ctor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/ctor.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// constexpr explicit +// basic_format_parse_context(basic_string_view fmt, +// size_t num_args = 0) noexcept + +#include +#include +#include + +#include "test_macros.h" + +template +constexpr void test(const CharT* fmt) { + // Validate the constructor is explicit. + static_assert( + !std::is_convertible_v, + std::basic_format_parse_context >); + static_assert( + !std::is_copy_constructible_v >); + static_assert( + !std::is_copy_assignable_v >); + // The move operations are implicitly deleted due to the + // deleted copy operations. + static_assert( + !std::is_move_constructible_v >); + static_assert( + !std::is_move_assignable_v >); + + ASSERT_NOEXCEPT( + std::basic_format_parse_context{std::basic_string_view{}}); + ASSERT_NOEXCEPT( + std::basic_format_parse_context{std::basic_string_view{}, 42}); + + { + std::basic_format_parse_context context(fmt); + assert(context.begin() == &fmt[0]); + assert(context.end() == &fmt[3]); + } + { + std::basic_string_view view{fmt}; + std::basic_format_parse_context context(view); + assert(context.begin() == view.begin()); + assert(context.end() == view.end()); + } +} + +constexpr bool test() { + test("abc"); + test(L"abc"); +#ifndef _LIBCPP_NO_HAS_CHAR8_T + test(u8"abc"); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(u"abc"); + test(U"abc"); +#endif + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/end.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/end.pass.cpp @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// constexpr end() const noexcept; + +#include +#include + +#include "test_macros.h" + +template +constexpr void test(const CharT* fmt) { + { + std::basic_format_parse_context context(fmt); + assert(context.end() == &fmt[3]); + ASSERT_NOEXCEPT(context.end()); + } + { + std::basic_string_view view{fmt}; + std::basic_format_parse_context context(view); + assert(context.end() == view.end()); + ASSERT_NOEXCEPT(context.end()); + } +} + +constexpr bool test() { + test("abc"); + test(L"abc"); +#ifndef _LIBCPP_NO_HAS_CHAR8_T + test(u8"abc"); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(u"abc"); + test(U"abc"); +#endif + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/next_arg_id.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/next_arg_id.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/next_arg_id.pass.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// 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: no-exceptions + +// This test requires the dylib support introduced in D92214. +// XFAIL: with_system_cxx_lib=macosx10.15 +// XFAIL: with_system_cxx_lib=macosx10.14 +// XFAIL: with_system_cxx_lib=macosx10.13 +// XFAIL: with_system_cxx_lib=macosx10.12 +// XFAIL: with_system_cxx_lib=macosx10.11 +// XFAIL: with_system_cxx_lib=macosx10.10 +// XFAIL: with_system_cxx_lib=macosx10.9 + +// + +// constexpr size_t next_arg_id(); + +#include +#include + +#include "test_macros.h" + +constexpr bool test() { + std::format_parse_context context(""); + for (size_t i = 0; i < 10; ++i) + assert(i == context.next_arg_id()); + + return true; +} + +void test_exception() { + std::format_parse_context context("", 1); + context.check_arg_id(0); + + try { + context.next_arg_id(); + assert(false); + } catch (const std::format_error& e) { + assert(strcmp(e.what(), "Using automatic argument numbering in manual " + "argument numbering mode") == 0); + return; + } + assert(false); +} + +int main(int, char**) { + test(); + test_exception(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/types.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/format/format.formatter/format.parse.ctx/types.compile.pass.cpp @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// 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-no-concepts + +// + +// Class typedefs: +// template +// class basic_format_parse_context { +// public: +// using char_type = charT; +// using const_iterator = typename basic_string_view::const_iterator; +// using iterator = const_iterator; +// } +// +// Namespace std typedefs: +// using format_parse_context = basic_format_parse_context; +// using wformat_parse_context = basic_format_parse_context; + +#include +#include + +#include "test_macros.h" + +template +constexpr void test() { + static_assert( + std::is_same_v::char_type, + CharT>); + static_assert(std::is_same_v< + typename std::basic_format_parse_context::const_iterator, + typename std::basic_string_view::const_iterator>); + static_assert( + std::is_same_v< + typename std::basic_format_parse_context::iterator, + typename std::basic_format_parse_context::const_iterator>); +} + +constexpr void test() { + test(); + test(); +#ifndef _LIBCPP_NO_HAS_CHAR8_T + test(); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS + test(); + test(); +#endif +} + +static_assert(std::is_same_v >); +static_assert(std::is_same_v >); + +// Required for MSVC internal test runner compatibility. +int main(int, char**) { return 0; }