Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 42 additions & 17 deletions stl/inc/format
Original file line number Diff line number Diff line change
Expand Up @@ -1827,9 +1827,10 @@ template <class _Handler>
class _Specs_checker : public _Handler {
private:
_Basic_format_arg_type _Arg_type;
// we'll see this if we get a modifier that requires an integer presentation type
// for types that can have either integer or non-integer presentation types (charT or bool)
bool _Need_arithmetic_presentation_type = false;
// We'll see this if we get a modifier that requires an integer presentation type
// for types that can have either integer or non-integer presentation types (charT or bool).
bool _Need_arithmetic_presentation_type = false;
bool _Need_arithmetic_or_pointer_presentation_type = false;

public:
constexpr explicit _Specs_checker(const _Handler& _Handler_inst, const _Basic_format_arg_type _Arg_type_)
Expand All @@ -1841,6 +1842,12 @@ public:
}
}

constexpr void _Require_numeric_or_pointer_argument() {
if (!_Is_arithmetic_fmt_type(_Arg_type) && _Arg_type != _Basic_format_arg_type::_Pointer_type) {
_Throw_format_error("Format specifier requires numeric or pointer argument.");
}
}

constexpr void _Check_precision() const {
if (_Is_integral_fmt_type(_Arg_type) || _Arg_type == _Basic_format_arg_type::_Pointer_type) {
_Throw_format_error("Precision not allowed for this argument type.");
Expand All @@ -1867,8 +1874,8 @@ public:
}

constexpr void _On_zero() {
_Need_arithmetic_presentation_type = true;
_Require_numeric_argument();
_Need_arithmetic_or_pointer_presentation_type = true;
_Require_numeric_or_pointer_argument();
_Handler::_On_zero();
}

Expand Down Expand Up @@ -1934,6 +1941,7 @@ public:
_Cat = _Presentation_type_category::_Floating;
break;
case 'p':
case 'P':
_Cat = _Presentation_type_category::_Pointer;
break;
default:
Expand Down Expand Up @@ -2027,6 +2035,11 @@ public:
&& _Cat != _Presentation_type_category::_Floating) {
_Throw_format_error("Modifier requires an integer presentation type for bool");
}

if (_Need_arithmetic_or_pointer_presentation_type && _Cat != _Presentation_type_category::_Integer
&& _Cat != _Presentation_type_category::_Floating && _Cat != _Presentation_type_category::_Pointer) {
_Throw_format_error("Zero modifier requires an arithmetic or pointer presentation type");
}
_Handler::_On_type(_Type);
}
};
Expand Down Expand Up @@ -3238,25 +3251,37 @@ _NODISCARD _OutputIt _Fmt_write(
template <class _CharT, class _OutputIt>
_NODISCARD _OutputIt _Fmt_write(
_OutputIt _Out, const void* const _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale) {
_STL_INTERNAL_CHECK(_Specs._Type == '\0' || _Specs._Type == 'p');
_STL_INTERNAL_CHECK(_Specs._Type == '\0' || _Specs._Type == 'p' || _Specs._Type == 'P');
_STL_INTERNAL_CHECK(_Specs._Sgn == _Fmt_sign::_None);
_STL_INTERNAL_CHECK(!_Specs._Alt);
_STL_INTERNAL_CHECK(_Specs._Precision == -1);
_STL_INTERNAL_CHECK(!_Specs._Leading_zero);
_STL_INTERNAL_CHECK(!_Specs._Localized);

// Since the bit width of 0 is 0x0, special-case it instead of complicating the math even more.
int _Width = 3;
if (_Value != nullptr) {
// Compute the bit width of the pointer (i.e. how many bits it takes to be represented).
// Add 3 to the bit width so we always round up on the division.
// Divide that by the amount of bits a hexit represents (log2(16) = log2(2^4) = 4).
// Add 2 for the 0x prefix.
_Width = 2 + (_STD bit_width(reinterpret_cast<uintptr_t>(_Value)) + 3) / 4;
char _Buffer[16]; // 16 hexits
const auto [_End, _Ec] = _STD to_chars(_Buffer, _STD end(_Buffer), reinterpret_cast<uintptr_t>(_Value), 16);
_STL_INTERNAL_CHECK(_Ec == errc{});
const int _Width = 2 + static_cast<int>(_End - _Buffer);

_CharT _Prefix[2] = {_CharT{'0'}, _CharT{'x'}};
if (_Specs._Type == 'P') {
_Prefix[1] = _CharT{'X'};
_Buffer_to_uppercase(_Buffer, _End);
}

const bool _Write_leading_zeroes = _Specs._Leading_zero && _Specs._Alignment == _Fmt_align::_None;
auto _Writer = [&](_OutputIt _Out) {
_Out = _RANGES copy(_Prefix, _Prefix + 2, _STD move(_Out)).out;
if (_Write_leading_zeroes && _Width < _Specs._Width) {
_Out = _RANGES fill_n(_STD move(_Out), _Specs._Width - _Width, _CharT{'0'});
}
return _STD _Widen_and_copy<_CharT>(_Buffer, _End, _STD move(_Out));
};

if (_Write_leading_zeroes) {
return _Writer(_STD move(_Out));
}

return _Write_aligned(_STD move(_Out), _Width, _Specs, _Fmt_align::_Right,
[=](_OutputIt _Out) { return _Fmt_write<_CharT>(_STD move(_Out), _Value); });
return _STD _Write_aligned(_STD move(_Out), _Width, _Specs, _Fmt_align::_Right, _Writer);
}

template <class _CharT, class _OutputIt>
Expand Down
3 changes: 2 additions & 1 deletion stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@
// P2432R1 Fix istream_view
// P2465R3 Standard Library Modules std And std.compat
// P2508R1 basic_format_string, format_string, wformat_string
// P2510R3 Formatting Pointers
// P2520R0 move_iterator<T*> Should Be A Random-Access Iterator
// P2538R1 ADL-Proof projected
// P2572R1 std::format Fill Character Allowances
Expand Down Expand Up @@ -1750,7 +1751,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect
#define __cpp_lib_erase_if 202002L

#ifdef __cpp_lib_concepts
#define __cpp_lib_format 202207L
#define __cpp_lib_format 202304L
#define __cpp_lib_format_uchar 202311L
#define __cpp_lib_freestanding_ranges 202306L
#endif // defined(__cpp_lib_concepts)
Expand Down
4 changes: 4 additions & 0 deletions tests/libcxx/expected_results.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp FAIL
# libc++ has not implemented P2505R5: "Monadic Functions for std::expected"
std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp FAIL

# libc++ has not implemented P2510R3: "Formatting Pointers"
std/utilities/format/format.functions/vformat.locale.pass FAIL
std/utilities/format/format.functions/vformat.pass FAIL

# libc++ doesn't implement P2588R3 barrier's Phase Completion Guarantees
std/language.support/support.limits/support.limits.general/barrier.version.compile.pass.cpp FAIL

Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,7 @@ tests\P2474R2_views_repeat
tests\P2474R2_views_repeat_death
tests\P2494R2_move_only_range_adaptors
tests\P2505R5_monadic_functions_for_std_expected
tests\P2510R3_text_formatting_pointers
tests\P2517R1_apply_conditional_noexcept
tests\P2538R1_adl_proof_std_projected
tests\P2609R3_relaxing_ranges_just_a_smidge
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -991,7 +991,7 @@ void test_pointer_specs() {
throw_helper(STR("{:#}"), nullptr);

// Leading zero
throw_helper(STR("{:0}"), nullptr);
assert(format(STR("{:05}"), nullptr) == STR("0x000"));

// Width
assert(format(STR("{:5}"), nullptr) == STR(" 0x0"));
Expand Down Expand Up @@ -1350,10 +1350,10 @@ void libfmt_formatter_test_zero_flag() {
assert(format(STR("{0:05}"), 42ull) == STR("00042"));
assert(format(STR("{0:07}"), -42.0) == STR("-000042"));
assert(format(STR("{0:07}"), -42.0l) == STR("-000042"));
assert(format(STR("{0:05}"), reinterpret_cast<void*>(0x42)) == STR("0x042"));
throw_helper(STR("{0:0"), 'c');
throw_helper(STR("{0:05}"), 'c');
throw_helper(STR("{0:05}"), STR("abc"));
throw_helper(STR("{0:05}"), reinterpret_cast<void*>(0x42));
}

template <class charT>
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P2510R3_text_formatting_pointers/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\concepts_20_matrix.lst
Loading