diff --git a/libcxx/include/__config b/libcxx/include/__config --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -490,6 +490,14 @@ # define _LIBCPP_SHORT_WCHAR 1 # endif +# if defined(_AIX) || (defined(__MVS__) && defined(__64BIT__)) +// AIX and 64-bit MVS must use structure _OptionalFill in basic_ios for compatibility. +# define _LIBCPP_BASIC_IOS_MUST_USE_OPTIONAL_FILL 1 +# elif defined(_WIN32) || defined(__APPLE__) || defined(__sun__) || defined(__linux__) || defined(__FreeBSD__) || \ + defined(__NetBSD__) || defined(__OpenBSD__) +# define _LIBCPP_BASIC_IOS_MUST_USE_OPTIONAL_FILL 0 +# endif + // Libc++ supports various implementations of std::random_device. // // _LIBCPP_USING_DEV_RANDOM diff --git a/libcxx/include/ios b/libcxx/include/ios --- a/libcxx/include/ios +++ b/libcxx/include/ios @@ -224,6 +224,7 @@ #include <__system_error/error_code.h> #include <__system_error/error_condition.h> #include <__system_error/system_error.h> +#include <__type_traits/conditional.h> #include <__utility/swap.h> #include <__verbose_abort> #include @@ -605,6 +606,31 @@ clear(__rdstate_); } +template +// Attribute 'packed' is used to keep the layout compatible with the previous +// definition of '__set_' in basic_ios on AIX. See D124555 for details. +struct _LIBCPP_PACKED _OptionalFill { + _OptionalFill() : __set_(false) { } + _OptionalFill& operator=(typename _Traits::int_type __x) { __set_ = true; __fill_val_ = __x; return *this; } + bool __is_set() const { return __set_; } + typename _Traits::int_type __fill() const { return __fill_val_; } + +private: + typename _Traits::int_type __fill_val_; + bool __set_; +}; + +template +struct _LIBCPP_PACKED _SentinelValueFill { + _SentinelValueFill() : __fill_val_(_Traits::eof()) { } + _SentinelValueFill& operator=(typename _Traits::int_type __x) { __fill_val_ = __x; return *this; } + bool __is_set() const { return __fill_val_ != _Traits::eof(); } + typename _Traits::int_type __fill() const { return __fill_val_; } + +private: + typename _Traits::int_type __fill_val_; +}; + template class _LIBCPP_TEMPLATE_VIS basic_ios : public ios_base @@ -691,7 +717,23 @@ void set_rdbuf(basic_streambuf* __sb); private: basic_ostream* __tie_; - mutable int_type __fill_; + +#if _LIBCPP_BASIC_IOS_MUST_USE_OPTIONAL_FILL + using _FillType = _OptionalFill<_Traits>; +#else +#if defined(_WIN32) + static const bool _OptOutForABICompat = true; +#else + static const bool _OptOutForABICompat = false; +#endif + + using _FillType = _If< + sizeof(char_type) >= sizeof(int_type) && !_OptOutForABICompat, + _OptionalFill<_Traits>, + _SentinelValueFill<_Traits> + >; +#endif + mutable _FillType __fill_; }; template @@ -713,7 +755,7 @@ { ios_base::init(__sb); __tie_ = nullptr; - __fill_ = traits_type::eof(); + __fill_ = widen(' '); } template @@ -785,9 +827,9 @@ _CharT basic_ios<_CharT, _Traits>::fill() const { - if (traits_type::eq_int_type(traits_type::eof(), __fill_)) + if (!__fill_.__is_set()) __fill_ = widen(' '); - return __fill_; + return __fill_.__fill(); } template @@ -795,9 +837,9 @@ _CharT basic_ios<_CharT, _Traits>::fill(char_type __ch) { - if (traits_type::eq_int_type(traits_type::eof(), __fill_)) + if (!__fill_.__is_set()) __fill_ = widen(' '); - char_type __r = __fill_; + char_type __r = __fill_.__fill(); __fill_ = __ch; return __r; } diff --git a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_eof.pass.cpp b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_eof.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_eof.pass.cpp @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Test that weof as a wchar_t value can be set as the fill character. + +// UNSUPPORTED: no-wide-characters +// REQUIRES: target=powerpc{{(64)?}}-ibm-aix || target=s390x-ibm-zos + +#include +#include +#include +#include + +template +struct testbuf : public std::basic_streambuf { + testbuf() {} +}; + +int main(int, char**) { + testbuf sb; + std::wostream os(&sb); + os << std::setfill((wchar_t)std::char_traits::eof()); + assert(os.fill() == (wchar_t)std::char_traits::eof()); + + return 0; +}