[libcxx-commits] [libcxx] [libcxx] Fix ODR violation in iostream. (PR #132125)

via libcxx-commits libcxx-commits at lists.llvm.org
Tue May 20 16:44:27 PDT 2025


https://github.com/matts1 updated https://github.com/llvm/llvm-project/pull/132125

>From 3dc4dd440f538726dbc652be0296a62974f31b0f Mon Sep 17 00:00:00 2001
From: Matt Stark <msta at google.com>
Date: Thu, 20 Mar 2025 10:28:26 +1100
Subject: [PATCH] [libcxx] Fix ODR violation in iostream.

Note: This commit does not need a test, we already validate it via:
* It compiles successfully after replacing `#include <istream>` with `#include <iostream>` in `iostream.cpp`
* It passes existing tests after removing the ODR violation exception from the allowlist
* There are already existing tests to validate that it runs correctly.

Also tested with -ftrivial-auto-var-init and confirmed that it doesn't affect this (probably since it's a global variable)
---
 libcxx/include/__ostream/basic_ostream.h      |  4 +
 libcxx/include/ios                            | 10 +++
 libcxx/include/istream                        |  4 +
 ...bcxxabi.v1.stable.exceptions.nonew.abilist | 16 ++--
 ...bcxxabi.v1.stable.exceptions.nonew.abilist | 16 ++--
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |  8 --
 ...bcxxabi.v1.stable.exceptions.nonew.abilist |  8 --
 ...xxabi.v1.stable.noexceptions.nonew.abilist |  8 --
 libcxx/src/iostream.cpp                       | 83 ++++++++++---------
 .../ios.base.cons/uninitialized.pass.cpp      | 32 +++++++
 10 files changed, 109 insertions(+), 80 deletions(-)
 create mode 100644 libcxx/test/libcxx/input.output/iostreams.base/ios.base/ios.base.cons/uninitialized.pass.cpp

diff --git a/libcxx/include/__ostream/basic_ostream.h b/libcxx/include/__ostream/basic_ostream.h
index 09ad401a48d60..17e74f45969fb 100644
--- a/libcxx/include/__ostream/basic_ostream.h
+++ b/libcxx/include/__ostream/basic_ostream.h
@@ -57,6 +57,10 @@ class basic_ostream : virtual public basic_ios<_CharT, _Traits> {
   }
   ~basic_ostream() override;
 
+  // Required by iostream to create cin as uninitialized.
+  _LIBCPP_HIDE_FROM_ABI explicit basic_ostream(__uninitialized_ios_tag __uninit)
+      : basic_ios<_CharT, _Traits>(__uninit) {}
+
   basic_ostream(const basic_ostream& __rhs)            = delete;
   basic_ostream& operator=(const basic_ostream& __rhs) = delete;
 
diff --git a/libcxx/include/ios b/libcxx/include/ios
index cf16d1cafc28b..7aef8b34e50f5 100644
--- a/libcxx/include/ios
+++ b/libcxx/include/ios
@@ -251,6 +251,10 @@ _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
 typedef ptrdiff_t streamsize;
 
+struct _LIBCPP_HIDE_FROM_ABI __uninitialized_ios_tag {
+  _LIBCPP_HIDE_FROM_ABI explicit __uninitialized_ios_tag() = default;
+};
+
 class _LIBCPP_EXPORTED_FROM_ABI ios_base {
 public:
   class _LIBCPP_EXPORTED_FROM_ABI failure;
@@ -371,6 +375,9 @@ protected:
     // for the details.
   }
 
+  // Intentionally do not make this constexpr. Members cannot be uninitialized when using constexpr.
+  _LIBCPP_HIDE_FROM_ABI ios_base(__uninitialized_ios_tag) {}
+
   void init(void* __sb);
   _LIBCPP_HIDE_FROM_ABI void* rdbuf() const { return __rdbuf_; }
 
@@ -619,6 +626,9 @@ protected:
     // purposefully does no initialization
     // since the destructor does nothing this does not have ios_base issues.
   }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR basic_ios(__uninitialized_ios_tag __uninit) : ios_base{__uninit} {}
+
   _LIBCPP_HIDE_FROM_ABI void init(basic_streambuf<char_type, traits_type>* __sb);
 
   _LIBCPP_HIDE_FROM_ABI void move(basic_ios& __rhs);
diff --git a/libcxx/include/istream b/libcxx/include/istream
index 4596eebf5ed22..cff8f2acfefc3 100644
--- a/libcxx/include/istream
+++ b/libcxx/include/istream
@@ -225,6 +225,10 @@ protected:
   }
 
 public:
+  // Required by iostream to create cin as uninitialized.
+  _LIBCPP_HIDE_FROM_ABI explicit basic_istream(__uninitialized_ios_tag __uninit)
+      : basic_ios<_CharT, _Traits>(__uninit) {}
+
   basic_istream(const basic_istream& __rhs)            = delete;
   basic_istream& operator=(const basic_istream& __rhs) = delete;
 
diff --git a/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
index 4b6f3548ce495..1fc1ac48c3ec3 100644
--- a/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/i686-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1223,7 +1223,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk134__construct_barrier_algorithm_baseERi', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk13cinE', 'size': 148, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk13cinE', 'size': 88, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk13pmr15memory_resourceD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk13pmr15memory_resourceD2Ev', 'type': 'FUNC'}
@@ -1291,9 +1291,9 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk14__fs10filesystem8__statusERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk14cerrE', 'size': 136, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt6__ndk14clogE', 'size': 136, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt6__ndk14coutE', 'size': 136, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk14cerrE', 'size': 84, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk14clogE', 'size': 84, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk14coutE', 'size': 84, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk14stodERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPj', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk14stodERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPj', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk14stofERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPj', 'type': 'FUNC'}
@@ -1302,7 +1302,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk14stoiERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPji', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk14stolERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPji', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk14stolERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPji', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk14wcinE', 'size': 148, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk14wcinE', 'size': 88, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk15alignEjjRPvRj', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk15ctypeIcE10table_sizeE', 'size': 4, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk15ctypeIcE13classic_tableEv', 'type': 'FUNC'}
@@ -1327,9 +1327,9 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk15stollERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPji', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk15stoulERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPji', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk15stoulERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPji', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk15wcerrE', 'size': 136, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt6__ndk15wclogE', 'size': 136, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt6__ndk15wcoutE', 'size': 136, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk15wcerrE', 'size': 84, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk15wclogE', 'size': 84, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk15wcoutE', 'size': 84, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEyPc', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
index 45f3d7c5904e8..460714757e846 100644
--- a/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1223,7 +1223,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk13cinE', 'size': 280, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk13cinE', 'size': 168, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk13pmr15memory_resourceD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk13pmr15memory_resourceD2Ev', 'type': 'FUNC'}
@@ -1291,9 +1291,9 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk14__fs10filesystem8__statusERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk14cerrE', 'size': 264, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt6__ndk14clogE', 'size': 264, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt6__ndk14coutE', 'size': 264, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk14cerrE', 'size': 160, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk14clogE', 'size': 160, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk14coutE', 'size': 160, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk14stodERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk14stodERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk14stofERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm', 'type': 'FUNC'}
@@ -1302,7 +1302,7 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk14stoiERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk14stolERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk14stolERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk14wcinE', 'size': 280, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk14wcinE', 'size': 168, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk15alignEmmRPvRm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk15ctypeIcE10table_sizeE', 'size': 8, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk15ctypeIcE13classic_tableEv', 'type': 'FUNC'}
@@ -1327,9 +1327,9 @@
 {'is_defined': True, 'name': '_ZNSt6__ndk15stollERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk15stoulERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk15stoulERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt6__ndk15wcerrE', 'size': 264, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt6__ndk15wclogE', 'size': 264, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt6__ndk15wcoutE', 'size': 264, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk15wcerrE', 'size': 160, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk15wclogE', 'size': 160, 'type': 'OBJECT'}
+{'is_defined': True, 'name': '_ZNSt6__ndk15wcoutE', 'size': 160, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt6__ndk16__itoa8__u64toaEmPc', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index de8cf6deef1df..f8470252162d9 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1237,7 +1237,6 @@
 {'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__13cinE', 'size': 400, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD2Ev', 'type': 'FUNC'}
@@ -1305,9 +1304,6 @@
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__statusERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__14cerrE', 'size': 384, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__14clogE', 'size': 384, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__14coutE', 'size': 384, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__14stodERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14stodERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14stofERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm', 'type': 'FUNC'}
@@ -1316,7 +1312,6 @@
 {'is_defined': True, 'name': '_ZNSt3__14stoiERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14stolERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14stolERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__14wcinE', 'size': 400, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__15alignEmmRPvRm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__15ctypeIcE10table_sizeE', 'size': 8, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__15ctypeIcE13classic_tableEv', 'type': 'FUNC'}
@@ -1341,9 +1336,6 @@
 {'is_defined': True, 'name': '_ZNSt3__15stollERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__15stoulERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__15stoulERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__15wcerrE', 'size': 384, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__15wclogE', 'size': 384, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 384, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__sortIRNS_6__lessIaaEEPaEEvT0_S5_T_', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index 679a0626d3268..3176047e267a0 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1235,7 +1235,6 @@
 {'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__13cinE', 'size': 280, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD2Ev', 'type': 'FUNC'}
@@ -1303,9 +1302,6 @@
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__statusERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__14cerrE', 'size': 264, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__14clogE', 'size': 264, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__14coutE', 'size': 264, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__14stodERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14stodERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14stofERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm', 'type': 'FUNC'}
@@ -1314,7 +1310,6 @@
 {'is_defined': True, 'name': '_ZNSt3__14stoiERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14stolERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14stolERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__14wcinE', 'size': 280, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__15alignEmmRPvRm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__15ctypeIcE10table_sizeE', 'size': 8, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__15ctypeIcE13classic_tableEv', 'type': 'FUNC'}
@@ -1341,9 +1336,6 @@
 {'is_defined': True, 'name': '_ZNSt3__15stollERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__15stoulERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__15stoulERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__15wcerrE', 'size': 264, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__15wclogE', 'size': 264, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 264, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
index 02bec0e7cbefb..9d9b69198900b 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist
@@ -1206,7 +1206,6 @@
 {'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__13cinE', 'size': 280, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD0Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD1Ev', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__13pmr15memory_resourceD2Ev', 'type': 'FUNC'}
@@ -1274,9 +1273,6 @@
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__removeERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__renameERKNS1_4pathES4_PNS_10error_codeE', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14__fs10filesystem8__statusERKNS1_4pathEPNS_10error_codeE', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__14cerrE', 'size': 264, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__14clogE', 'size': 264, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__14coutE', 'size': 264, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__14stodERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14stodERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14stofERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPm', 'type': 'FUNC'}
@@ -1285,7 +1281,6 @@
 {'is_defined': True, 'name': '_ZNSt3__14stoiERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14stolERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__14stolERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__14wcinE', 'size': 280, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__15alignEmmRPvRm', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__15ctypeIcE10table_sizeE', 'size': 8, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__15ctypeIcE13classic_tableEv', 'type': 'FUNC'}
@@ -1312,9 +1307,6 @@
 {'is_defined': True, 'name': '_ZNSt3__15stollERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__15stoulERKNS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEPmi', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__15stoulERKNS_12basic_stringIwNS_11char_traitsIwEENS_9allocatorIwEEEEPmi', 'type': 'FUNC'}
-{'is_defined': True, 'name': '_ZNSt3__15wcerrE', 'size': 264, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__15wclogE', 'size': 264, 'type': 'OBJECT'}
-{'is_defined': True, 'name': '_ZNSt3__15wcoutE', 'size': 264, 'type': 'OBJECT'}
 {'is_defined': True, 'name': '_ZNSt3__16__clocEv', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u32toaEjPc', 'type': 'FUNC'}
 {'is_defined': True, 'name': '_ZNSt3__16__itoa8__u64toaEmPc', 'type': 'FUNC'}
diff --git a/libcxx/src/iostream.cpp b/libcxx/src/iostream.cpp
index 28d50ee779f2c..ea05067f0c19d 100644
--- a/libcxx/src/iostream.cpp
+++ b/libcxx/src/iostream.cpp
@@ -9,21 +9,19 @@
 #include "std_stream.h"
 
 #include <__memory/construct_at.h>
-#include <__ostream/basic_ostream.h>
-#include <istream>
+#include <iostream>
 
 #define ABI_NAMESPACE_STR _LIBCPP_TOSTRING(_LIBCPP_ABI_NAMESPACE)
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 _LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
 
-template <class StreamT, class BufferT>
+template <class BufferT, typename StreamT, StreamT& stream>
 union stream_data {
   constexpr stream_data() {}
   constexpr ~stream_data() {}
+  // Make this a struct inside a union so it can be uninitialized.
   struct {
-    // The stream has to be the first element, since that's referenced by the stream declarations in <iostream>
-    StreamT stream;
     BufferT buffer;
     mbstate_t mb;
   };
@@ -51,31 +49,36 @@ union stream_data {
         "?" #var "@" ABI_NAMESPACE_STR "@std@@3V?$" #StreamT                                                           \
         "@" CHAR_MANGLING(CharT) "U?$char_traits@" CHAR_MANGLING(CharT) "@" ABI_NAMESPACE_STR "@std@@@12 at A")
 #else
-#  define STREAM(StreamT, BufferT, CharT, var) STRING_DATA_CONSTINIT stream_data<StreamT<CharT>, BufferT<CharT>> var
-#endif
 
-// These definitions and the declarations in <iostream> technically cause ODR violations, since they have different
-// types (stream_data and {i,o}stream respectively). This means that <iostream> should never be included in this TU.
+// To avoid ODR violations without breaking the ABI, we need to define it as a StreamT<CharT>.
+// However, since initialization order of statics is arbitrary, we could run DoIOSInit first and then
+// construct the stream for a second time, overwriting the data in it.
+// To avoid this, we call a constructor overload that specifically avoids initializing any members.
+#  define STREAM(StreamT, BufferT, CharT, var)                                                                         \
+    StreamT<CharT> var{std::__uninitialized_ios_tag{}};                                                                \
+    stream_data<BufferT<CharT>, StreamT<CharT>, var> sd_##var;
+#endif
 
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_istream, __stdinbuf, char, cin);
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, char, cout);
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, char, cerr);
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, char, clog);
+_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_istream, __stdinbuf, char, cin)
+_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, char, cout)
+_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, char, cerr)
+_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, char, clog)
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_istream, __stdinbuf, wchar_t, wcin);
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout);
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr);
-_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog);
+_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_istream, __stdinbuf, wchar_t, wcin)
+_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, wchar_t, wcout)
+_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, wchar_t, wcerr)
+_LIBCPP_EXPORTED_FROM_ABI STREAM(basic_ostream, __stdoutbuf, wchar_t, wclog)
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
 // Pretend we're inside a system header so the compiler doesn't flag the use of the init_priority
 // attribute with a value that's reserved for the implementation (we're the implementation).
 #include "iostream_init.h"
 
-// On Windows the TLS storage for locales needs to be initialized before we create
-// the standard streams, otherwise it may not be alive during program termination
-// when we flush the streams.
-static void force_locale_initialization() {
+    // Clang-format indents this incorrectly, no clue why.
+    // On Windows the TLS storage for locales needs to be initialized before we create
+    // the standard streams, otherwise it may not be alive during program termination
+    // when we flush the streams.
+    static void force_locale_initialization() {
 #if defined(_LIBCPP_MSVCRT_LIKE)
   static bool once = []() {
     auto loc = __locale::__newlocale(_LIBCPP_ALL_MASK, "C", 0);
@@ -99,34 +102,34 @@ class DoIOSInit {
 DoIOSInit::DoIOSInit() {
   force_locale_initialization();
 
-  cin.init(stdin);
-  cout.init(stdout);
-  cerr.init(stderr);
-  clog.init(stderr);
+  sd_cin.init(stdin);
+  sd_cout.init(stdout);
+  sd_cerr.init(stderr);
+  sd_clog.init(stderr);
 
-  cin.stream.tie(&cout.stream);
-  std::unitbuf(cerr.stream);
-  cerr.stream.tie(&cout.stream);
+  cin.tie(&cout);
+  std::unitbuf(cerr);
+  cerr.tie(&cout);
 
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-  wcin.init(stdin);
-  wcout.init(stdout);
-  wcerr.init(stderr);
-  wclog.init(stderr);
-
-  wcin.stream.tie(&wcout.stream);
-  std::unitbuf(wcerr.stream);
-  wcerr.stream.tie(&wcout.stream);
+  sd_wcin.init(stdin);
+  sd_wcout.init(stdout);
+  sd_wcerr.init(stderr);
+  sd_wclog.init(stderr);
+
+  wcin.tie(&wcout);
+  std::unitbuf(wcerr);
+  wcerr.tie(&wcout);
 #endif
 }
 
 DoIOSInit::~DoIOSInit() {
-  cout.stream.flush();
-  clog.stream.flush();
+  cout.flush();
+  clog.flush();
 
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-  wcout.stream.flush();
-  wclog.stream.flush();
+  wcout.flush();
+  wclog.flush();
 #endif
 }
 
diff --git a/libcxx/test/libcxx/input.output/iostreams.base/ios.base/ios.base.cons/uninitialized.pass.cpp b/libcxx/test/libcxx/input.output/iostreams.base/ios.base/ios.base.cons/uninitialized.pass.cpp
new file mode 100644
index 0000000000000..76e75425f5e49
--- /dev/null
+++ b/libcxx/test/libcxx/input.output/iostreams.base/ios.base/ios.base.cons/uninitialized.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: asserts
+
+#include <cassert>
+#include <cstddef>
+#include <iostream>
+#include <memory>
+
+template <typename T>
+void run_test(std::byte value) {
+  std::array<std::byte, sizeof(T)> initial;
+  initial.fill(value);
+
+  auto constructed = initial;
+  std::construct_at(reinterpret_cast<T*>(constructed.data()), std::__uninitialized_ios_tag());
+
+  assert(constructed == initial);
+}
+
+int main(int, char**) {
+  run_test<std::basic_istream<char>>(std::byte(0));
+  run_test<std::basic_istream<char>>(std::byte(255));
+  run_test<std::basic_ostream<char>>(std::byte(0));
+  run_test<std::basic_ostream<char>>(std::byte(255));
+}



More information about the libcxx-commits mailing list