From 236fea1f008e1b4216d5dd5f3a5e1ab3051b794a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BB=D0=B0=D0=B4=D0=B8=D1=81=D0=BB=D0=B0=D0=B2=20?= =?UTF-8?q?=D0=A9=D0=B0=D0=BF=D0=BE=D0=B2?= Date: Tue, 27 Oct 2020 13:55:26 +0500 Subject: [PATCH 1/9] Workaround bugs in gcc 8 --- include/fmt/color.h | 9 +++++---- include/fmt/compile.h | 16 ++++++++-------- include/fmt/core.h | 15 ++++++++------- include/fmt/locale.h | 8 ++++---- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index 78910589..e89d4e3a 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -589,10 +589,11 @@ OutputIt vformat_to( \endrst */ template >::value&& - detail::is_string::value)> -inline OutputIt format_to(OutputIt out, const text_style& ts, - const S& format_str, Args&&... args) { + bool enable = (detail::is_output_iterator>::value&& + detail::is_string::value)> +inline auto format_to(OutputIt out, const text_style& ts, + const S& format_str, Args&&... args) -> + typename std::enable_if::type { return vformat_to(out, ts, to_string_view(format_str), fmt::make_args_checked(format_str, args...)); } diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 7db610d9..8b79a530 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -667,14 +667,14 @@ OutputIt format_to(OutputIt out, const S&, const Args&... args) { return format_to(out, compiled, args...); } -template ::value&& - std::is_base_of::value)> -format_to_n_result format_to_n(OutputIt out, size_t n, - const CompiledFormat& cf, - const Args&... args) { +template +typename std::enable_if<(detail::is_output_iterator< + OutputIt, typename CompiledFormat::char_type>::value&& + std::is_base_of::value), + format_to_n_result +>::type format_to_n(OutputIt out, size_t n, + const CompiledFormat& cf, const Args&... args) { auto it = format_to(detail::truncating_iterator(out, n), cf, args...); return {it.base(), it.count()}; diff --git a/include/fmt/core.h b/include/fmt/core.h index 2903be5a..4278f0e1 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1968,10 +1968,11 @@ inline void vprint_mojibake(std::FILE*, string_view, format_args) {} // GCC 8 and earlier cannot handle std::back_insert_iterator with // vformat_to(...) overload, so SFINAE on iterator type instead. template , - FMT_ENABLE_IF(detail::is_output_iterator::value)> -OutputIt vformat_to( + bool enable = detail::is_output_iterator::value> +auto vformat_to( OutputIt out, const S& format_str, - basic_format_args>> args) { + basic_format_args>> args) -> + typename std::enable_if::type { decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out)); detail::vformat_to(buf, to_string_view(format_str), args); return detail::get_iterator(buf); @@ -2023,10 +2024,10 @@ inline format_to_n_result vformat_to_n( \endrst */ template >::value)> -inline format_to_n_result format_to_n(OutputIt out, size_t n, - const S& format_str, - const Args&... args) { + bool enable = detail::is_output_iterator>::value> +inline auto format_to_n(OutputIt out, size_t n, + const S& format_str, const Args&... args) -> + typename std::enable_if>::type { const auto& vargs = fmt::make_args_checked(format_str, args...); return vformat_to_n(out, n, to_string_view(format_str), vargs); } diff --git a/include/fmt/locale.h b/include/fmt/locale.h index 517f6505..7301bf92 100644 --- a/include/fmt/locale.h +++ b/include/fmt/locale.h @@ -51,10 +51,10 @@ inline OutputIt vformat_to( } template , - FMT_ENABLE_IF(detail::is_output_iterator::value)> -inline OutputIt format_to(OutputIt out, const std::locale& loc, - const S& format_str, Args&&... args) { + bool enable = detail::is_output_iterator>::value> +inline auto format_to(OutputIt out, const std::locale& loc, + const S& format_str, Args&&... args) -> + typename std::enable_if::type { const auto& vargs = fmt::make_args_checked(format_str, args...); return vformat_to(out, loc, to_string_view(format_str), vargs); } From 28a8eae8506ba67775d74a530af913c015ae5197 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 27 Oct 2020 07:19:28 -0700 Subject: [PATCH 2/9] Cleanup --- include/fmt/color.h | 8 ++++---- include/fmt/compile.h | 15 ++++++++------- include/fmt/format.h | 15 +++++++-------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index e89d4e3a..94e3419d 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -589,10 +589,10 @@ OutputIt vformat_to( \endrst */ template >::value&& - detail::is_string::value)> -inline auto format_to(OutputIt out, const text_style& ts, - const S& format_str, Args&&... args) -> + bool enable = detail::is_output_iterator>::value&& + detail::is_string::value> +inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, + Args&&... args) -> typename std::enable_if::type { return vformat_to(out, ts, to_string_view(format_str), fmt::make_args_checked(format_str, args...)); diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 8b79a530..3a33b020 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -668,13 +668,14 @@ OutputIt format_to(OutputIt out, const S&, const Args&... args) { } template -typename std::enable_if<(detail::is_output_iterator< - OutputIt, typename CompiledFormat::char_type>::value&& - std::is_base_of::value), - format_to_n_result ->::type format_to_n(OutputIt out, size_t n, - const CompiledFormat& cf, const Args&... args) { +auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf, + const Args&... args) -> + typename std::enable_if< + detail::is_output_iterator::value && + std::is_base_of::value, + format_to_n_result>::type { auto it = format_to(detail::truncating_iterator(out, n), cf, args...); return {it.base(), it.count()}; diff --git a/include/fmt/format.h b/include/fmt/format.h index fbe50450..6b03e857 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3056,8 +3056,7 @@ struct format_handler : detail::error_handler { basic_format_parse_context parse_context; Context context; - format_handler(OutputIt out, - basic_string_view str, + format_handler(OutputIt out, basic_string_view str, basic_format_args format_args, detail::locale_ref loc) : parse_context(str), context(out, format_args, loc) {} @@ -3080,8 +3079,8 @@ struct format_handler : detail::error_handler { FMT_INLINE void on_replacement_field(int id, const Char*) { auto arg = get_arg(context, id); context.advance_to(visit_format_arg( - default_arg_formatter{ - context.out(), context.args(), context.locale()}, + default_arg_formatter{context.out(), context.args(), + context.locale()}, arg)); } @@ -3105,8 +3104,8 @@ struct format_handler : detail::error_handler { if (begin == end || *begin != '}') on_error("missing '}' in format string"); } - context.advance_to( - visit_format_arg(arg_formatter(context, &parse_context, &specs), arg)); + context.advance_to(visit_format_arg( + arg_formatter(context, &parse_context, &specs), arg)); return begin; } }; @@ -3776,8 +3775,8 @@ void detail::vformat_to( arg); return; } - format_handler> h( - out, format_str, args, loc); + format_handler> h(out, format_str, args, + loc); parse_format_string(format_str, h); } From 556a1cfb34000978e819df73db2c53cecfee1d20 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 27 Oct 2020 07:44:12 -0700 Subject: [PATCH 3/9] Instantiate to_decimal to make gcc lto happy (#1955) --- src/format.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/format.cc b/src/format.cc index 72713416..88565e60 100644 --- a/src/format.cc +++ b/src/format.cc @@ -23,6 +23,11 @@ int format_float(char* buf, std::size_t size, const char* format, int precision, return precision < 0 ? snprintf_ptr(buf, size, format, value) : snprintf_ptr(buf, size, format, precision, value); } + +template dragonbox::decimal_fp dragonbox::to_decimal(float x) + FMT_NOEXCEPT; +template dragonbox::decimal_fp dragonbox::to_decimal(double x) + FMT_NOEXCEPT; } // namespace detail template struct FMT_INSTANTIATION_DEF_API detail::basic_data; From 5c045049320ecd966fdd9faff8d0d01adc6017d5 Mon Sep 17 00:00:00 2001 From: OptoCloud <26223094+OptoCloud@users.noreply.github.com> Date: Wed, 28 Oct 2020 10:12:08 +0100 Subject: [PATCH 4/9] Removed [-Wsign-conversion] warning in GCC --- include/fmt/format-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index b7cb3209..8155bd78 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -1901,7 +1901,7 @@ template <> struct cache_accessor { uint64_t pow5 = data::powers_of_5_64[offset]; uint128_wrapper recovered_cache = umul128(base_cache.high(), pow5); uint128_wrapper middle_low = - umul128(base_cache.low() - (kb < 0 ? 1 : 0), pow5); + umul128(base_cache.low() - (kb < 0 ? 1u : 0u), pow5); recovered_cache += middle_low.high(); From 69a84198b09a2b96d76c090ecbe225f5ae7602f9 Mon Sep 17 00:00:00 2001 From: Tobias Hammer Date: Thu, 29 Oct 2020 15:08:06 +0100 Subject: [PATCH 5/9] Remove accidental parenthesis (#1968) fails only when FMT_BUILTIN_CTZLL is not defined --- include/fmt/format-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 8155bd78..e3b3490c 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -1756,7 +1756,7 @@ inline bool divisible_by_power_of_2(uint64_t x, int exp) FMT_NOEXCEPT { #ifdef FMT_BUILTIN_CTZLL return FMT_BUILTIN_CTZLL(x) >= exp; #else - return exp < num_bits()) && x == ((x >> exp) << exp); + return exp < num_bits() && x == ((x >> exp) << exp); #endif } From 425778aa67a25d847bfe11a7364f0c89db1898ff Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Thu, 29 Oct 2020 08:02:40 -0700 Subject: [PATCH 6/9] Fix ABI compatibility (#1961) --- include/fmt/format-inl.h | 12 ++++++++++-- include/fmt/format.h | 13 +++++++++---- src/format.cc | 25 +++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index e3b3490c..5d466eeb 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -261,11 +261,19 @@ const uint64_t basic_data::powers_of_10_64[] = { 10000000000000000000ULL}; template -const uint32_t basic_data::zero_or_powers_of_10_32[] = {0, 0, +const uint32_t basic_data::zero_or_powers_of_10_32[] = {0, FMT_POWERS_OF_10(1)}; - template const uint64_t basic_data::zero_or_powers_of_10_64[] = { + 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + +template +const uint32_t basic_data::zero_or_powers_of_10_32_new[] = { + 0, 0, FMT_POWERS_OF_10(1)}; + +template +const uint64_t basic_data::zero_or_powers_of_10_64_new[] = { 0, 0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ULL), 10000000000000000000ULL}; diff --git a/include/fmt/format.h b/include/fmt/format.h index 6b03e857..13b8da30 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -866,8 +866,8 @@ template struct FMT_EXTERN_TEMPLATE_API divtest_table_entry { // Static data is placed in this class template for the header-only config. template struct FMT_EXTERN_TEMPLATE_API basic_data { static const uint64_t powers_of_10_64[]; - static const uint32_t zero_or_powers_of_10_32[]; - static const uint64_t zero_or_powers_of_10_64[]; + static const uint32_t zero_or_powers_of_10_32_new[]; + static const uint64_t zero_or_powers_of_10_64_new[]; static const uint64_t grisu_pow10_significands[]; static const int16_t grisu_pow10_exponents[]; static const divtest_table_entry divtest_table_for_pow5_32[]; @@ -891,6 +891,10 @@ template struct FMT_EXTERN_TEMPLATE_API basic_data { static const char signs[]; static const char left_padding_shifts[5]; static const char right_padding_shifts[5]; + + // DEPRECATED! These are for ABI compatibility. + static const uint32_t zero_or_powers_of_10_32[]; + static const uint64_t zero_or_powers_of_10_64[]; }; // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). @@ -917,7 +921,7 @@ struct data : basic_data<> {}; inline int count_digits(uint64_t n) { // https://github.com/fmtlib/format-benchmark/blob/master/digits10 auto t = bsr2log10(FMT_BUILTIN_CLZLL(n | 1) ^ 63); - return t - (n < data::zero_or_powers_of_10_64[t]); + return t - (n < data::zero_or_powers_of_10_64_new[t]); } #else // Fallback version of count_digits used when __builtin_clz is not available. @@ -984,7 +988,7 @@ template <> int count_digits<4>(detail::fallback_uintptr n); // Optional version of count_digits for better performance on 32-bit platforms. inline int count_digits(uint32_t n) { auto t = bsr2log10(FMT_BUILTIN_CLZ(n | 1) ^ 31); - return t - (n < data::zero_or_powers_of_10_32[t]); + return t - (n < data::zero_or_powers_of_10_32_new[t]); } #endif @@ -3785,6 +3789,7 @@ extern template void detail::vformat_to(detail::buffer&, string_view, basic_format_args, detail::locale_ref); namespace detail { + extern template FMT_API std::string grouping_impl(locale_ref loc); extern template FMT_API std::string grouping_impl(locale_ref loc); extern template FMT_API char thousands_sep_impl(locale_ref loc); diff --git a/src/format.cc b/src/format.cc index 88565e60..bca87b03 100644 --- a/src/format.cc +++ b/src/format.cc @@ -28,6 +28,31 @@ template dragonbox::decimal_fp dragonbox::to_decimal(float x) FMT_NOEXCEPT; template dragonbox::decimal_fp dragonbox::to_decimal(double x) FMT_NOEXCEPT; + +// DEPRECATED! This function exists for ABI compatibility. +template +typename basic_format_context>, + Char>::iterator +vformat_to(buffer& buf, basic_string_view format_str, + basic_format_args>>, + type_identity_t>> + args) { + using iterator = std::back_insert_iterator>; + using context = basic_format_context< + std::back_insert_iterator>>, + type_identity_t>; + auto out = iterator(buf); + format_handler h(out, format_str, args, {}); + parse_format_string(format_str, h); + return out; +} +template basic_format_context>, + char>::iterator +vformat_to(buffer&, string_view, + basic_format_args>>, + type_identity_t>>); } // namespace detail template struct FMT_INSTANTIATION_DEF_API detail::basic_data; From 563cbb6c212f170e8c9d969f23aa998e504ff975 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 31 Oct 2020 07:51:39 -0700 Subject: [PATCH 7/9] Add a macro to workaround clang/gcc ABI incompatibility on ARM --- include/fmt/core.h | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 4278f0e1..a962f375 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1929,7 +1929,14 @@ template class basic_format_args { } }; -/** An alias to ``basic_format_args``. */ +#ifdef FMT_ARM_ABI_COMPATIBILITY +/** An alias to ``basic_format_args``. */ +// Separate types would result in shorter symbols but break ABI compatibility +// between clang and gcc on ARM (#1919). +using format_args = basic_format_args; +using wformat_args = basic_format_args; +#else +// DEPRECATED! These are kept for ABI compatibility. // It is a separate type rather than an alias to make symbols readable. struct format_args : basic_format_args { template @@ -1938,6 +1945,7 @@ struct format_args : basic_format_args { struct wformat_args : basic_format_args { using basic_format_args::basic_format_args; }; +#endif namespace detail { @@ -1969,10 +1977,9 @@ inline void vprint_mojibake(std::FILE*, string_view, format_args) {} // vformat_to(...) overload, so SFINAE on iterator type instead. template , bool enable = detail::is_output_iterator::value> -auto vformat_to( - OutputIt out, const S& format_str, - basic_format_args>> args) -> - typename std::enable_if::type { +auto vformat_to(OutputIt out, const S& format_str, + basic_format_args>> args) + -> typename std::enable_if::type { decltype(detail::get_buffer(out)) buf(detail::get_buffer_init(out)); detail::vformat_to(buf, to_string_view(format_str), args); return detail::get_iterator(buf); @@ -2025,8 +2032,8 @@ inline format_to_n_result vformat_to_n( */ template >::value> -inline auto format_to_n(OutputIt out, size_t n, - const S& format_str, const Args&... args) -> +inline auto format_to_n(OutputIt out, size_t n, const S& format_str, + const Args&... args) -> typename std::enable_if>::type { const auto& vargs = fmt::make_args_checked(format_str, args...); return vformat_to_n(out, n, to_string_view(format_str), vargs); From 5d3f0741e3d3dd781a5a74d043ae61fce095c3d6 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 1 Nov 2020 06:21:00 -0800 Subject: [PATCH 8/9] Update changelog and bump version --- ChangeLog.rst | 24 ++++++++++++++++++++++++ include/fmt/core.h | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/ChangeLog.rst b/ChangeLog.rst index f9a2d8eb..4babdd08 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -1,3 +1,27 @@ +7.1.1 - TBD +----------- + +* Fixed ABI compatibility with 7.0.x + (`#1961 `_). + +* Added the ``FMT_ARM_ABI_COMPATIBILITY`` macro to work around ABI + incompatibility between GCC and Clang on ARM + (`#1919 `_). + +* Worked around a SFINAE bug in GCC 8 + (`#1957 `_). + +* Fixed linkage errors when building with GCC's LTO + (`#1955 `_). + +* Fixed a compilation error when building without ``__builtin_clz`` or equivalent + (`#1968 `_). + Thanks `@tohammer (Tobias Hammer) `_. + +* Fixed a sign conversion warning + (`#1964 `_). + Thanks `@OptoCloud `_. + 7.1.0 - 2020-10-25 ------------------ diff --git a/include/fmt/core.h b/include/fmt/core.h index a962f375..60e49425 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -18,7 +18,7 @@ #include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 70100 +#define FMT_VERSION 70101 #ifdef __clang__ # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) From 5f7f7b954d0e958f3d94f3d906177c6a1090f079 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 1 Nov 2020 06:30:39 -0800 Subject: [PATCH 9/9] Update version --- ChangeLog.rst | 4 ++-- doc/build.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog.rst b/ChangeLog.rst index 4babdd08..a5b776cf 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -1,5 +1,5 @@ -7.1.1 - TBD ------------ +7.1.1 - 2020-11-01 +------------------ * Fixed ABI compatibility with 7.0.x (`#1961 `_). diff --git a/doc/build.py b/doc/build.py index 7152638b..85eaaa81 100755 --- a/doc/build.py +++ b/doc/build.py @@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE from distutils.version import LooseVersion -versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0'] +versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0', '7.1.1'] def pip_install(package, commit=None, **kwargs): "Install package using pip."