[libcxx-commits] [libcxx] [libc++] Avoid type-punning locale in ios_base (PR #193507)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Apr 22 07:05:15 PDT 2026
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/193507
While there is currently a comment that all members of `ios_base` must
be scalars, I see no reason that is a correct statement. However, the
only place where this is relevant is `locale __loc_`. This patch
replaces the current `void*` implementation with `union { locale __loc_ }`
to simplify `ios.cpp`.
>From 870ae43baa20ef5d52fe0c3aa060386ebb822e54 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Wed, 22 Apr 2026 12:42:59 +0200
Subject: [PATCH] [libc++] Avoid type-punning locale in ios_base
While there is currently a comment that all members of `ios_base` must
be scalars, I see no reason that is a correct statement. However, the
only place where this is relevant is `locale __loc_`. This patch
replaces the current `void*` implementation with `union { locale __loc_ }`
to simplify `ios.cpp`.
---
libcxx/include/__locale | 3 +++
libcxx/include/ios | 7 +++---
libcxx/src/ios.cpp | 47 ++++++++++++++++-------------------------
3 files changed, 25 insertions(+), 32 deletions(-)
diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index 5b1787451fb25..8b6b48ccd6b59 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -118,6 +118,9 @@ private:
template <class>
friend struct __no_destroy;
+
+ friend ios_base;
+
_LIBCPP_HIDE_FROM_ABI explicit locale(__private_constructor_tag, __imp* __loc) : __locale_(__loc) {}
void __install_ctor(const locale&, facet*, long);
diff --git a/libcxx/include/ios b/libcxx/include/ios
index 9cf0aa8998ed1..3c26d92822367 100644
--- a/libcxx/include/ios
+++ b/libcxx/include/ios
@@ -361,7 +361,7 @@ public:
}
protected:
- _LIBCPP_HIDE_FROM_ABI ios_base() : __loc_(nullptr) {
+ _LIBCPP_HIDE_FROM_ABI ios_base() : __loc_(__private_constructor_tag(), nullptr) {
// Purposefully does no initialization
//
// Except for the locale, this is a sentinel to avoid destroying
@@ -386,14 +386,15 @@ protected:
_LIBCPP_HIDE_FROM_ABI void set_rdbuf(void* __sb) { __rdbuf_ = __sb; }
private:
- // All data members must be scalars
fmtflags __fmtflags_;
streamsize __precision_;
streamsize __width_;
iostate __rdstate_;
iostate __exceptions_;
void* __rdbuf_;
- void* __loc_;
+ union {
+ locale __loc_;
+ };
event_callback* __fn_;
int* __index_;
size_t __event_size_;
diff --git a/libcxx/src/ios.cpp b/libcxx/src/ios.cpp
index 077389eafd61b..5baff89a86410 100644
--- a/libcxx/src/ios.cpp
+++ b/libcxx/src/ios.cpp
@@ -102,18 +102,13 @@ void ios_base::__call_callbacks(event ev) {
// locale
locale ios_base::imbue(const locale& newloc) {
- static_assert(sizeof(locale) == sizeof(__loc_), "");
- locale& loc_storage = *reinterpret_cast<locale*>(&__loc_);
- locale oldloc = loc_storage;
- loc_storage = newloc;
+ locale loc = newloc;
+ std::swap(__loc_, loc);
__call_callbacks(imbue_event);
- return oldloc;
+ return loc;
}
-locale ios_base::getloc() const {
- const locale& loc_storage = *reinterpret_cast<const locale*>(&__loc_);
- return loc_storage;
-}
+locale ios_base::getloc() const { return __loc_; }
// xalloc
#if _LIBCPP_HAS_C_ATOMIC_IMP && _LIBCPP_HAS_THREADS
@@ -201,11 +196,10 @@ void ios_base::register_callback(event_callback fn, int index) {
ios_base::~ios_base() {
// Avoid UB when not properly initialized. See ios_base::ios_base for
// more information.
- if (!__loc_)
+ if (!__loc_.__locale_)
return;
__call_callbacks(erase_event);
- locale& loc_storage = *reinterpret_cast<locale*>(&__loc_);
- loc_storage.~locale();
+ __loc_.~locale();
free(__fn_);
free(__index_);
free(__iarray_);
@@ -277,12 +271,10 @@ void ios_base::copyfmt(const ios_base& rhs) {
std::__throw_bad_alloc();
}
// Got everything we need. Copy everything but __rdstate_, __rdbuf_ and __exceptions_
- __fmtflags_ = rhs.__fmtflags_;
- __precision_ = rhs.__precision_;
- __width_ = rhs.__width_;
- locale& lhs_loc = *reinterpret_cast<locale*>(&__loc_);
- const locale& rhs_loc = *reinterpret_cast<const locale*>(&rhs.__loc_);
- lhs_loc = rhs_loc;
+ __fmtflags_ = rhs.__fmtflags_;
+ __precision_ = rhs.__precision_;
+ __width_ = rhs.__width_;
+ __loc_ = rhs.__loc_;
if (__event_cap_ < rhs.__event_size_) {
free(__fn_);
__fn_ = new_callbacks.release();
@@ -312,14 +304,13 @@ void ios_base::copyfmt(const ios_base& rhs) {
void ios_base::move(ios_base& rhs) {
// *this is uninitialized
- __fmtflags_ = rhs.__fmtflags_;
- __precision_ = rhs.__precision_;
- __width_ = rhs.__width_;
- __rdstate_ = rhs.__rdstate_;
- __exceptions_ = rhs.__exceptions_;
- __rdbuf_ = 0;
- locale& rhs_loc = *reinterpret_cast<locale*>(&rhs.__loc_);
- ::new (&__loc_) locale(rhs_loc);
+ __fmtflags_ = rhs.__fmtflags_;
+ __precision_ = rhs.__precision_;
+ __width_ = rhs.__width_;
+ __rdstate_ = rhs.__rdstate_;
+ __exceptions_ = rhs.__exceptions_;
+ __rdbuf_ = 0;
+ ::new (&__loc_) locale(rhs.__loc_);
__fn_ = rhs.__fn_;
rhs.__fn_ = 0;
__index_ = rhs.__index_;
@@ -348,9 +339,7 @@ void ios_base::swap(ios_base& rhs) noexcept {
std::swap(__width_, rhs.__width_);
std::swap(__rdstate_, rhs.__rdstate_);
std::swap(__exceptions_, rhs.__exceptions_);
- locale& lhs_loc = *reinterpret_cast<locale*>(&__loc_);
- locale& rhs_loc = *reinterpret_cast<locale*>(&rhs.__loc_);
- std::swap(lhs_loc, rhs_loc);
+ std::swap(__loc_, rhs.__loc_);
std::swap(__fn_, rhs.__fn_);
std::swap(__index_, rhs.__index_);
std::swap(__event_size_, rhs.__event_size_);
More information about the libcxx-commits
mailing list