[libcxx-commits] [libcxx] [libc++] Prevent cycle from including __mbstate_t.h within wchar.h (PR #200940)
Zibi Sarbinowski via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Aug 20 10:48:51 PDT 2026
https://github.com/zibi2 updated https://github.com/llvm/llvm-project/pull/200940
>From adaee84ead8c3a2d69bf1d572e86fdc7da99b255 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Mon, 17 Aug 2026 09:38:06 -0400
Subject: [PATCH 1/4] [libc++] Remove __mbstate_t.h and inline platform logic
into __std_mbstate_t.h
Remove __mbstate_t.h and __cxx03/__mbstate_t.h entirely. The only
legitimate need for mbstate_t outside of <wchar.h>/<uchar.h> is in
__std_mbstate_t.h (used by iosfwd, char_traits, locale, etc. to get
std::mbstate_t). Inline the platform-detection logic directly into
__std_mbstate_t.h instead of indirecting through __mbstate_t.h.
Also remove the unconditional fallback includes of <__mbstate_t.h> and
<stddef.h> from wchar.h, uchar.h and __cxx03/wchar.h. On platforms
that provide a system <wchar.h> (including z/OS), mbstate_t comes from
the system header via include_next. On platforms without it, libc++ is
not responsible for providing mbstate_t.
This eliminates the circular inclusion that caused module build failures
(redefinition of wchar.h shims like __libcpp_wcschr) when __mbstate_t.h
fell through to include_next <wchar.h> inside the module system, which
re-entered libc++'s wchar.h without the _LIBCPP_WCHAR_H guard being set.
Removes the std_private_mbstate_t and cxx03_std_private_mbstate_t
module map entries and the file list entries in CMakeLists.txt.
---
libcxx/include/CMakeLists.txt | 2 -
libcxx/include/__cxx03/__mbstate_t.h | 54 ----------------------
libcxx/include/__cxx03/__std_mbstate_t.h | 27 ++++++++++-
libcxx/include/__cxx03/module.modulemap | 4 --
libcxx/include/__cxx03/wchar.h | 2 -
libcxx/include/__mbstate_t.h | 58 ------------------------
libcxx/include/__std_mbstate_t.h | 29 +++++++++++-
libcxx/include/module.modulemap.in | 5 --
libcxx/include/uchar.h | 3 --
libcxx/include/wchar.h | 3 --
10 files changed, 54 insertions(+), 133 deletions(-)
delete mode 100644 libcxx/include/__cxx03/__mbstate_t.h
delete mode 100644 libcxx/include/__mbstate_t.h
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index a1d6a09aa6b20..3de0bef7dccf6 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -590,7 +590,6 @@ set(files
__math/rounding_functions.h
__math/traits.h
__math/trigonometric_functions.h
- __mbstate_t.h
__mdspan/aligned_accessor.h
__mdspan/default_accessor.h
__mdspan/extents.h
@@ -1368,7 +1367,6 @@ set(files
__cxx03/__math/rounding_functions.h
__cxx03/__math/traits.h
__cxx03/__math/trigonometric_functions.h
- __cxx03/__mbstate_t.h
__cxx03/__memory/addressof.h
__cxx03/__memory/align.h
__cxx03/__memory/aligned_alloc.h
diff --git a/libcxx/include/__cxx03/__mbstate_t.h b/libcxx/include/__cxx03/__mbstate_t.h
deleted file mode 100644
index 6c7cfaaa8fe5b..0000000000000
--- a/libcxx/include/__cxx03/__mbstate_t.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// -*- C++ -*-
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP___CXX03___MBSTATE_T_H
-#define _LIBCPP___CXX03___MBSTATE_T_H
-
-#include <__cxx03/__config>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-#endif
-
-// The goal of this header is to provide mbstate_t without requiring all of
-// <uchar.h> or <wchar.h>. It's also used by the libc++ versions of <uchar.h>
-// and <wchar.h> to get mbstate_t when the C library doesn't provide <uchar.h>
-// or <wchar.h>, hence the #include_next of those headers instead of #include.
-// (e.g. if <wchar.h> isn't present in the C library, the libc++ <wchar.h>
-// will include this header. This header needs to not turn around and cyclically
-// include <__cxx03/wchar.h>, but fall through to <uchar.h>.)
-//
-// This does not define std::mbstate_t -- this only brings in the declaration
-// in the global namespace.
-
-// We define this here to support older versions of glibc <wchar.h> that do
-// not define this for clang. This is also set in libc++'s <wchar.h> header,
-// and we need to do so here too to avoid a different function signature given
-// a different include order.
-#ifdef __cplusplus
-# define __CORRECT_ISO_CPP_WCHAR_H_PROTO
-#endif
-
-#if defined(_LIBCPP_HAS_MUSL_LIBC)
-# define __NEED_mbstate_t
-# include <bits/alltypes.h>
-# undef __NEED_mbstate_t
-#elif __has_include(<bits/types/mbstate_t.h>)
-# include <bits/types/mbstate_t.h> // works on most Unixes
-#elif __has_include(<sys/_types/_mbstate_t.h>)
-# include <sys/_types/_mbstate_t.h> // works on Darwin
-#elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) && __has_include_next(<wchar.h>)
-# include_next <wchar.h> // fall back to the C standard provider of mbstate_t
-#elif __has_include_next(<uchar.h>)
-# include_next <uchar.h> // <uchar.h> is also required to make mbstate_t visible
-#else
-# error "We don't know how to get the definition of mbstate_t without <wchar.h> on your platform."
-#endif
-
-#endif // _LIBCPP___CXX03___MBSTATE_T_H
diff --git a/libcxx/include/__cxx03/__std_mbstate_t.h b/libcxx/include/__cxx03/__std_mbstate_t.h
index 7a81d1bd024ae..1595d845cc9e1 100644
--- a/libcxx/include/__cxx03/__std_mbstate_t.h
+++ b/libcxx/include/__cxx03/__std_mbstate_t.h
@@ -11,7 +11,6 @@
#define _LIBCPP___CXX03___STD_MBSTATE_T_H
#include <__cxx03/__config>
-#include <__cxx03/__mbstate_t.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -20,6 +19,32 @@
// The goal of this header is to provide std::mbstate_t without requiring all
// of <cuchar> or <cwchar>.
+// We define this here to support older versions of glibc <wchar.h> that do
+// not define this for clang. This is also set in libc++'s <wchar.h> header,
+// and we need to do so here too to avoid a different function signature given
+// a different include order.
+#ifdef __cplusplus
+# define __CORRECT_ISO_CPP_WCHAR_H_PROTO
+#endif
+
+#if defined(_LIBCPP_HAS_MUSL_LIBC)
+# define __NEED_mbstate_t
+# include <bits/alltypes.h>
+# undef __NEED_mbstate_t
+#elif __has_include(<bits/types/mbstate_t.h>)
+# include <bits/types/mbstate_t.h> // works on most Unixes
+#elif __has_include(<sys/_types/_mbstate_t.h>)
+# include <sys/_types/_mbstate_t.h> // works on Darwin
+#elif __has_include(<bits/mbstate_t.h>)
+# include <bits/mbstate_t.h> // works for Android
+#elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) && __has_include_next(<wchar.h>)
+# include_next <wchar.h> // fall back to the C standard provider of mbstate_t
+#elif __has_include_next(<uchar.h>)
+# include_next <uchar.h> // <uchar.h> is also required to make mbstate_t visible
+#else
+# error "We don't know how to get the definition of mbstate_t without <wchar.h> on your platform."
+#endif
+
_LIBCPP_BEGIN_NAMESPACE_STD
using ::mbstate_t _LIBCPP_USING_IF_EXISTS;
diff --git a/libcxx/include/__cxx03/module.modulemap b/libcxx/include/__cxx03/module.modulemap
index 717926a46a55d..fa6b55e5c0532 100644
--- a/libcxx/include/__cxx03/module.modulemap
+++ b/libcxx/include/__cxx03/module.modulemap
@@ -590,10 +590,6 @@ module cxx03_std_private_locale [system] {
header "__locale"
export *
}
-module cxx03_std_private_mbstate_t [system] {
- header "__mbstate_t.h"
- export *
-}
module cxx03_std_private_node_handle [system] {
header "__node_handle"
export *
diff --git a/libcxx/include/__cxx03/wchar.h b/libcxx/include/__cxx03/wchar.h
index f30b18df9c9f7..0da6b276b176e 100644
--- a/libcxx/include/__cxx03/wchar.h
+++ b/libcxx/include/__cxx03/wchar.h
@@ -125,8 +125,6 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
# if __has_include_next(<wchar.h>)
# include_next <wchar.h>
-# else
-# include <__cxx03/__mbstate_t.h> // make sure we have mbstate_t regardless of the existence of <wchar.h>
# endif
// Determine whether we have const-correct overloads for wcschr and friends.
diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
deleted file mode 100644
index 1ce5f0cd6da1d..0000000000000
--- a/libcxx/include/__mbstate_t.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// -*- C++ -*-
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP___MBSTATE_T_H
-#define _LIBCPP___MBSTATE_T_H
-
-#include <__config>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-#endif
-
-// The goal of this header is to provide mbstate_t without requiring all of
-// <uchar.h> or <wchar.h>. It's also used by the libc++ versions of <uchar.h>
-// and <wchar.h> to get mbstate_t when the C library doesn't provide <uchar.h>
-// or <wchar.h>, hence the #include_next of those headers instead of #include.
-// (e.g. if <wchar.h> isn't present in the C library, the libc++ <wchar.h>
-// will include this header. This header needs to not turn around and cyclically
-// include <wchar.h>, but fall through to <uchar.h>.)
-//
-// This does not define std::mbstate_t -- this only brings in the declaration
-// in the global namespace.
-
-// We define this here to support older versions of glibc <wchar.h> that do
-// not define this for clang. This is also set in libc++'s <wchar.h> header,
-// and we need to do so here too to avoid a different function signature given
-// a different include order.
-#ifdef __cplusplus
-# define __CORRECT_ISO_CPP_WCHAR_H_PROTO
-#endif
-
-#if _LIBCPP_HAS_MUSL_LIBC
-# define __NEED_mbstate_t
-# include <bits/alltypes.h>
-# undef __NEED_mbstate_t
-#elif _LIBCPP_LIBC_LLVM_LIBC
-# include <llvm-libc-types/mbstate_t.h>
-#elif __has_include(<bits/types/mbstate_t.h>)
-# include <bits/types/mbstate_t.h> // works on most Unixes
-#elif __has_include(<sys/_types/_mbstate_t.h>)
-# include <sys/_types/_mbstate_t.h> // works on Darwin
-#elif __has_include(<bits/mbstate_t.h>)
-# include <bits/mbstate_t.h> // works for Android
-#elif __has_include_next(<wchar.h>)
-# include_next <wchar.h> // use the C standard provider of mbstate_t if present
-#elif __has_include_next(<uchar.h>)
-# include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
-#else
-# error "We don't know how to get the definition of mbstate_t on your platform."
-#endif
-
-#endif // _LIBCPP___MBSTATE_T_H
diff --git a/libcxx/include/__std_mbstate_t.h b/libcxx/include/__std_mbstate_t.h
index e79cc789fddf9..7990328312f35 100644
--- a/libcxx/include/__std_mbstate_t.h
+++ b/libcxx/include/__std_mbstate_t.h
@@ -11,7 +11,6 @@
#define _LIBCPP___STD_MBSTATE_T_H
#include <__config>
-#include <__mbstate_t.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -20,6 +19,34 @@
// The goal of this header is to provide std::mbstate_t without requiring all
// of <cuchar> or <cwchar>.
+// We define this here to support older versions of glibc <wchar.h> that do
+// not define this for clang. This is also set in libc++'s <wchar.h> header,
+// and we need to do so here too to avoid a different function signature given
+// a different include order.
+#ifdef __cplusplus
+# define __CORRECT_ISO_CPP_WCHAR_H_PROTO
+#endif
+
+#if _LIBCPP_HAS_MUSL_LIBC
+# define __NEED_mbstate_t
+# include <bits/alltypes.h>
+# undef __NEED_mbstate_t
+#elif _LIBCPP_LIBC_LLVM_LIBC
+# include <llvm-libc-types/mbstate_t.h>
+#elif __has_include(<bits/types/mbstate_t.h>)
+# include <bits/types/mbstate_t.h> // works on most Unixes
+#elif __has_include(<sys/_types/_mbstate_t.h>)
+# include <sys/_types/_mbstate_t.h> // works on Darwin
+#elif __has_include(<bits/mbstate_t.h>)
+# include <bits/mbstate_t.h> // works for Android
+#elif __has_include_next(<wchar.h>)
+# include_next <wchar.h> // use the C standard provider of mbstate_t if present
+#elif __has_include_next(<uchar.h>)
+# include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
+#else
+# error "We don't know how to get the definition of mbstate_t on your platform."
+#endif
+
_LIBCPP_BEGIN_NAMESPACE_STD
using ::mbstate_t _LIBCPP_USING_IF_EXISTS;
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4512cc71153ca..f8228bbb59779 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2566,8 +2566,3 @@ module std_wctype_h {
export *
}
-// This header is used by other C compatibility headers so it needs to be in its own module.
-module std_private_mbstate_t {
- header "__mbstate_t.h"
- export *
-}
diff --git a/libcxx/include/uchar.h b/libcxx/include/uchar.h
index 01e1d57db7454..00a57dfdb640b 100644
--- a/libcxx/include/uchar.h
+++ b/libcxx/include/uchar.h
@@ -49,9 +49,6 @@ size_t c32rtomb(char* s, char32_t c32, mbstate_t* ps);
// get the declaration of mbstate_t too.
# if __has_include_next(<uchar.h>)
# include_next <uchar.h>
-# else
-# include <__mbstate_t.h>
-# include <stddef.h>
# endif
# endif // _LIBCPP_CXX03_LANG
diff --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index e5844249bdf7e..25c4c236529b5 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -119,9 +119,6 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
# ifndef _LIBCPP_WCHAR_H
# define _LIBCPP_WCHAR_H
-# include <__mbstate_t.h> // provide mbstate_t
-# include <stddef.h> // provide size_t
-
// Determine whether we have const-correct overloads for wcschr and friends.
# if defined(_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_)
# define _LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS 1
>From cf20f43ca745d6af33deb8972365ff8ee72c2193 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Tue, 18 Aug 2026 12:11:31 -0400
Subject: [PATCH 2/4] [libc++] Remove __std_mbstate_t.h and replace with direct
wchar.h includes
Remove __std_mbstate_t.h and __cxx03/__std_mbstate_t.h. All includers
are updated to use <wchar.h> directly, which provides mbstate_t on all
supported platforms via the platform-detection chain already present
in libc++'s wchar.h.
This is a follow-up to the removal of __mbstate_t.h: since the platform
detection logic was already inlined into __std_mbstate_t.h in the
previous commit, and since wchar.h on all supported platforms (including
z/OS) provides mbstate_t, there is no longer any need for the
intermediate __std_mbstate_t.h indirection.
Files updated:
- iosfwd, __locale, __string/char_traits.h
- __locale_dir/support/{aix,bsd_like,linux,newlib,openbsd,no_locale/conversions}.h
- __cxx03/__locale, __cxx03/iosfwd
- module.modulemap.in, __cxx03/module.modulemap, CMakeLists.txt
---
libcxx/include/CMakeLists.txt | 2 -
libcxx/include/__cxx03/__locale | 2 +-
libcxx/include/__cxx03/__std_mbstate_t.h | 54 ------------------
libcxx/include/__cxx03/iosfwd | 2 +-
libcxx/include/__cxx03/module.modulemap | 4 --
libcxx/include/__locale_dir/codecvt.h | 2 +-
libcxx/include/__locale_dir/support/aix.h | 2 +-
.../include/__locale_dir/support/bsd_like.h | 2 +-
libcxx/include/__locale_dir/support/linux.h | 2 +-
libcxx/include/__locale_dir/support/newlib.h | 2 +-
.../support/no_locale/conversions.h | 2 +-
libcxx/include/__locale_dir/support/openbsd.h | 2 +-
.../include/__locale_dir/utf8_conversions.h | 2 +-
libcxx/include/__std_mbstate_t.h | 56 -------------------
libcxx/include/__string/char_traits.h | 2 +-
libcxx/include/iosfwd | 2 +-
libcxx/include/module.modulemap.in | 4 --
17 files changed, 12 insertions(+), 132 deletions(-)
delete mode 100644 libcxx/include/__cxx03/__std_mbstate_t.h
delete mode 100644 libcxx/include/__std_mbstate_t.h
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 3de0bef7dccf6..026dcdcc513af 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -794,7 +794,6 @@ set(files
__ranges/zip_transform_view.h
__ranges/zip_view.h
__split_buffer
- __std_mbstate_t.h
__stop_token/atomic_unique_lock.h
__stop_token/intrusive_list_view.h
__stop_token/intrusive_shared_ptr.h
@@ -1441,7 +1440,6 @@ set(files
__cxx03/__random/weibull_distribution.h
__cxx03/__split_buffer
__cxx03/__std_clang_module
- __cxx03/__std_mbstate_t.h
__cxx03/__string/char_traits.h
__cxx03/__string/constexpr_c_functions.h
__cxx03/__string/extern_template_lists.h
diff --git a/libcxx/include/__cxx03/__locale b/libcxx/include/__cxx03/__locale
index e9cbc1f2d7683..92f3edd80c354 100644
--- a/libcxx/include/__cxx03/__locale
+++ b/libcxx/include/__cxx03/__locale
@@ -30,7 +30,7 @@
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
# include <__cxx03/cwchar>
#else
-# include <__cxx03/__std_mbstate_t.h>
+# include <wchar.h>
#endif
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/__cxx03/__std_mbstate_t.h b/libcxx/include/__cxx03/__std_mbstate_t.h
deleted file mode 100644
index 1595d845cc9e1..0000000000000
--- a/libcxx/include/__cxx03/__std_mbstate_t.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// -*- C++ -*-
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP___CXX03___STD_MBSTATE_T_H
-#define _LIBCPP___CXX03___STD_MBSTATE_T_H
-
-#include <__cxx03/__config>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-#endif
-
-// The goal of this header is to provide std::mbstate_t without requiring all
-// of <cuchar> or <cwchar>.
-
-// We define this here to support older versions of glibc <wchar.h> that do
-// not define this for clang. This is also set in libc++'s <wchar.h> header,
-// and we need to do so here too to avoid a different function signature given
-// a different include order.
-#ifdef __cplusplus
-# define __CORRECT_ISO_CPP_WCHAR_H_PROTO
-#endif
-
-#if defined(_LIBCPP_HAS_MUSL_LIBC)
-# define __NEED_mbstate_t
-# include <bits/alltypes.h>
-# undef __NEED_mbstate_t
-#elif __has_include(<bits/types/mbstate_t.h>)
-# include <bits/types/mbstate_t.h> // works on most Unixes
-#elif __has_include(<sys/_types/_mbstate_t.h>)
-# include <sys/_types/_mbstate_t.h> // works on Darwin
-#elif __has_include(<bits/mbstate_t.h>)
-# include <bits/mbstate_t.h> // works for Android
-#elif !defined(_LIBCPP_HAS_NO_WIDE_CHARACTERS) && __has_include_next(<wchar.h>)
-# include_next <wchar.h> // fall back to the C standard provider of mbstate_t
-#elif __has_include_next(<uchar.h>)
-# include_next <uchar.h> // <uchar.h> is also required to make mbstate_t visible
-#else
-# error "We don't know how to get the definition of mbstate_t without <wchar.h> on your platform."
-#endif
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-using ::mbstate_t _LIBCPP_USING_IF_EXISTS;
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP___CXX03___STD_MBSTATE_T_H
diff --git a/libcxx/include/__cxx03/iosfwd b/libcxx/include/__cxx03/iosfwd
index 674e901c30650..7f7d3999ae843 100644
--- a/libcxx/include/__cxx03/iosfwd
+++ b/libcxx/include/__cxx03/iosfwd
@@ -114,7 +114,7 @@ using wosyncstream = basic_osyncstream<wchar_t>; // C++20
#include <__cxx03/__fwd/sstream.h>
#include <__cxx03/__fwd/streambuf.h>
#include <__cxx03/__fwd/string.h>
-#include <__cxx03/__std_mbstate_t.h>
+#include <wchar.h>
#include <__cxx03/version>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/__cxx03/module.modulemap b/libcxx/include/__cxx03/module.modulemap
index fa6b55e5c0532..ba88846cc230b 100644
--- a/libcxx/include/__cxx03/module.modulemap
+++ b/libcxx/include/__cxx03/module.modulemap
@@ -598,10 +598,6 @@ module cxx03_std_private_split_buffer [system] {
header "__split_buffer"
export *
}
-module cxx03_std_private_std_mbstate_t [system] {
- header "__std_mbstate_t.h"
- export *
-}
module cxx03_std_private_tree [system] {
header "__tree"
export *
diff --git a/libcxx/include/__locale_dir/codecvt.h b/libcxx/include/__locale_dir/codecvt.h
index c601a6ee62cd2..a404c03dc246c 100644
--- a/libcxx/include/__locale_dir/codecvt.h
+++ b/libcxx/include/__locale_dir/codecvt.h
@@ -21,7 +21,7 @@
# if _LIBCPP_HAS_WIDE_CHARACTERS
# include <cwchar>
# else
-# include <__std_mbstate_t.h>
+# include <wchar.h>
# endif
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/__locale_dir/support/aix.h b/libcxx/include/__locale_dir/support/aix.h
index 4ab52793ae3c1..c14e62aa904bf 100644
--- a/libcxx/include/__locale_dir/support/aix.h
+++ b/libcxx/include/__locale_dir/support/aix.h
@@ -11,7 +11,7 @@
#include <__config>
#include <__cstddef/size_t.h>
-#include <__std_mbstate_t.h>
+#include <wchar.h>
#include <__utility/forward.h>
#include <clocale> // std::lconv
#include <cstdio>
diff --git a/libcxx/include/__locale_dir/support/bsd_like.h b/libcxx/include/__locale_dir/support/bsd_like.h
index 6632ca51e1eaa..891e476522653 100644
--- a/libcxx/include/__locale_dir/support/bsd_like.h
+++ b/libcxx/include/__locale_dir/support/bsd_like.h
@@ -11,7 +11,7 @@
#include <__config>
#include <__cstddef/size_t.h>
-#include <__std_mbstate_t.h>
+#include <wchar.h>
#include <__utility/forward.h>
#include <clocale> // std::lconv
#include <ctype.h>
diff --git a/libcxx/include/__locale_dir/support/linux.h b/libcxx/include/__locale_dir/support/linux.h
index 6a91a001c239c..816cca1b702e5 100644
--- a/libcxx/include/__locale_dir/support/linux.h
+++ b/libcxx/include/__locale_dir/support/linux.h
@@ -11,7 +11,7 @@
#include <__config>
#include <__cstddef/size_t.h>
-#include <__std_mbstate_t.h>
+#include <wchar.h>
#include <__utility/forward.h>
#include <clocale> // std::lconv
#include <cstdio>
diff --git a/libcxx/include/__locale_dir/support/newlib.h b/libcxx/include/__locale_dir/support/newlib.h
index 7fe48572b4587..2c887b40f2c06 100644
--- a/libcxx/include/__locale_dir/support/newlib.h
+++ b/libcxx/include/__locale_dir/support/newlib.h
@@ -11,7 +11,7 @@
#include <__config>
#include <__cstddef/size_t.h>
-#include <__std_mbstate_t.h>
+#include <wchar.h>
#include <clocale> // std::lconv
#include <cstdio>
#include <cstdlib>
diff --git a/libcxx/include/__locale_dir/support/no_locale/conversions.h b/libcxx/include/__locale_dir/support/no_locale/conversions.h
index bb501515c4d4e..a9495e2b8b1ec 100644
--- a/libcxx/include/__locale_dir/support/no_locale/conversions.h
+++ b/libcxx/include/__locale_dir/support/no_locale/conversions.h
@@ -11,7 +11,7 @@
#include <__config>
#include <__cstddef/size_t.h>
-#include <__std_mbstate_t.h>
+#include <wchar.h>
#include <cstdlib>
#if _LIBCPP_HAS_WIDE_CHARACTERS
# include <cwchar>
diff --git a/libcxx/include/__locale_dir/support/openbsd.h b/libcxx/include/__locale_dir/support/openbsd.h
index 0ec5b12f2ebdd..157c230b767cb 100644
--- a/libcxx/include/__locale_dir/support/openbsd.h
+++ b/libcxx/include/__locale_dir/support/openbsd.h
@@ -12,7 +12,7 @@
#include <__config>
#include <__cstddef/size_t.h>
-#include <__std_mbstate_t.h>
+#include <wchar.h>
#include <__utility/forward.h>
#include <clocale> // std::lconv
#include <ctype.h>
diff --git a/libcxx/include/__locale_dir/utf8_conversions.h b/libcxx/include/__locale_dir/utf8_conversions.h
index a4a32700a8ad7..68d435488f64d 100644
--- a/libcxx/include/__locale_dir/utf8_conversions.h
+++ b/libcxx/include/__locale_dir/utf8_conversions.h
@@ -15,7 +15,7 @@
# include <__cstddef/size_t.h>
# include <__locale_dir/codecvt.h>
-# include <__std_mbstate_t.h>
+# include <wchar.h>
# include <stdexcept>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/__std_mbstate_t.h b/libcxx/include/__std_mbstate_t.h
deleted file mode 100644
index 7990328312f35..0000000000000
--- a/libcxx/include/__std_mbstate_t.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// -*- C++ -*-
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef _LIBCPP___STD_MBSTATE_T_H
-#define _LIBCPP___STD_MBSTATE_T_H
-
-#include <__config>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-#endif
-
-// The goal of this header is to provide std::mbstate_t without requiring all
-// of <cuchar> or <cwchar>.
-
-// We define this here to support older versions of glibc <wchar.h> that do
-// not define this for clang. This is also set in libc++'s <wchar.h> header,
-// and we need to do so here too to avoid a different function signature given
-// a different include order.
-#ifdef __cplusplus
-# define __CORRECT_ISO_CPP_WCHAR_H_PROTO
-#endif
-
-#if _LIBCPP_HAS_MUSL_LIBC
-# define __NEED_mbstate_t
-# include <bits/alltypes.h>
-# undef __NEED_mbstate_t
-#elif _LIBCPP_LIBC_LLVM_LIBC
-# include <llvm-libc-types/mbstate_t.h>
-#elif __has_include(<bits/types/mbstate_t.h>)
-# include <bits/types/mbstate_t.h> // works on most Unixes
-#elif __has_include(<sys/_types/_mbstate_t.h>)
-# include <sys/_types/_mbstate_t.h> // works on Darwin
-#elif __has_include(<bits/mbstate_t.h>)
-# include <bits/mbstate_t.h> // works for Android
-#elif __has_include_next(<wchar.h>)
-# include_next <wchar.h> // use the C standard provider of mbstate_t if present
-#elif __has_include_next(<uchar.h>)
-# include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
-#else
-# error "We don't know how to get the definition of mbstate_t on your platform."
-#endif
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-using ::mbstate_t _LIBCPP_USING_IF_EXISTS;
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP___STD_MBSTATE_T_H
diff --git a/libcxx/include/__string/char_traits.h b/libcxx/include/__string/char_traits.h
index d98595030e6a5..927d89cc5ce90 100644
--- a/libcxx/include/__string/char_traits.h
+++ b/libcxx/include/__string/char_traits.h
@@ -21,7 +21,7 @@
#include <__functional/hash.h>
#include <__functional/identity.h>
#include <__iterator/iterator_traits.h>
-#include <__std_mbstate_t.h>
+#include <wchar.h>
#include <__string/constexpr_c_functions.h>
#include <__type_traits/is_constant_evaluated.h>
#include <__utility/is_pointer_in_range.h>
diff --git a/libcxx/include/iosfwd b/libcxx/include/iosfwd
index 7f7c9417d3e70..492c5a478770c 100644
--- a/libcxx/include/iosfwd
+++ b/libcxx/include/iosfwd
@@ -117,7 +117,7 @@ using wosyncstream = basic_osyncstream<wchar_t>; // C++20
# include <__fwd/sstream.h>
# include <__fwd/streambuf.h>
# include <__fwd/string.h>
-# include <__std_mbstate_t.h>
+# include <wchar.h>
# include <version>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index f8228bbb59779..46b45cf75ccdb 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2473,10 +2473,6 @@ module std {
header "__tree"
export std.memory.unique_ptr
}
- module std_mbstate_t {
- header "__std_mbstate_t.h"
- export *
- }
module log_hardening_failure {
header "__log_hardening_failure"
}
>From 0dd5e87e4df3313a4804dddb5a5d262821edc9a5 Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Thu, 20 Aug 2026 10:12:51 -0400
Subject: [PATCH 3/4] [libc++] Fix include order for <wchar.h> after removal of
__std_mbstate_t.h
The previous commit replaced <__std_mbstate_t.h> (an __-prefixed internal
header) with <wchar.h> in several files, but placed <wchar.h> before other
__-prefixed headers, violating the llvm-include-order clang-tidy rule which
requires all <__...> internal headers to precede system headers.
Move <wchar.h> to after the last <__...> include in each affected file:
- __locale_dir/support/aix.h
- __locale_dir/support/bsd_like.h
- __locale_dir/support/linux.h
- __locale_dir/support/openbsd.h
- __string/char_traits.h
- iosfwd
- __cxx03/iosfwd
---
libcxx/include/__cxx03/iosfwd | 2 +-
libcxx/include/__locale_dir/support/aix.h | 2 +-
libcxx/include/__locale_dir/support/bsd_like.h | 2 +-
libcxx/include/__locale_dir/support/linux.h | 2 +-
libcxx/include/__locale_dir/support/newlib.h | 2 +-
libcxx/include/__locale_dir/support/no_locale/conversions.h | 2 +-
libcxx/include/__locale_dir/support/openbsd.h | 2 +-
libcxx/include/__locale_dir/utf8_conversions.h | 2 +-
libcxx/include/__string/char_traits.h | 2 +-
libcxx/include/iosfwd | 2 +-
10 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/libcxx/include/__cxx03/iosfwd b/libcxx/include/__cxx03/iosfwd
index 7f7d3999ae843..32c1e15a0f31c 100644
--- a/libcxx/include/__cxx03/iosfwd
+++ b/libcxx/include/__cxx03/iosfwd
@@ -114,8 +114,8 @@ using wosyncstream = basic_osyncstream<wchar_t>; // C++20
#include <__cxx03/__fwd/sstream.h>
#include <__cxx03/__fwd/streambuf.h>
#include <__cxx03/__fwd/string.h>
-#include <wchar.h>
#include <__cxx03/version>
+#include <wchar.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
diff --git a/libcxx/include/__locale_dir/support/aix.h b/libcxx/include/__locale_dir/support/aix.h
index c14e62aa904bf..43664ce78d85c 100644
--- a/libcxx/include/__locale_dir/support/aix.h
+++ b/libcxx/include/__locale_dir/support/aix.h
@@ -11,7 +11,6 @@
#include <__config>
#include <__cstddef/size_t.h>
-#include <wchar.h>
#include <__utility/forward.h>
#include <clocale> // std::lconv
#include <cstdio>
@@ -20,6 +19,7 @@
#include <langinfo.h>
#include <string.h>
#include <time.h>
+#include <wchar.h>
#if _LIBCPP_HAS_WIDE_CHARACTERS
# include <cwchar>
# include <wctype.h>
diff --git a/libcxx/include/__locale_dir/support/bsd_like.h b/libcxx/include/__locale_dir/support/bsd_like.h
index 891e476522653..64ce5453a962b 100644
--- a/libcxx/include/__locale_dir/support/bsd_like.h
+++ b/libcxx/include/__locale_dir/support/bsd_like.h
@@ -11,7 +11,6 @@
#include <__config>
#include <__cstddef/size_t.h>
-#include <wchar.h>
#include <__utility/forward.h>
#include <clocale> // std::lconv
#include <ctype.h>
@@ -20,6 +19,7 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <wchar.h>
#if _LIBCPP_HAS_WIDE_CHARACTERS
# include <wchar.h>
# include <wctype.h>
diff --git a/libcxx/include/__locale_dir/support/linux.h b/libcxx/include/__locale_dir/support/linux.h
index 816cca1b702e5..bd2bc19d39f04 100644
--- a/libcxx/include/__locale_dir/support/linux.h
+++ b/libcxx/include/__locale_dir/support/linux.h
@@ -11,7 +11,6 @@
#include <__config>
#include <__cstddef/size_t.h>
-#include <wchar.h>
#include <__utility/forward.h>
#include <clocale> // std::lconv
#include <cstdio>
@@ -21,6 +20,7 @@
#include <stdarg.h>
#include <string.h>
#include <time.h>
+#include <wchar.h>
#if _LIBCPP_HAS_WIDE_CHARACTERS
# include <cwchar>
# include <wctype.h>
diff --git a/libcxx/include/__locale_dir/support/newlib.h b/libcxx/include/__locale_dir/support/newlib.h
index 2c887b40f2c06..5dc2aa066f7bb 100644
--- a/libcxx/include/__locale_dir/support/newlib.h
+++ b/libcxx/include/__locale_dir/support/newlib.h
@@ -11,7 +11,6 @@
#include <__config>
#include <__cstddef/size_t.h>
-#include <wchar.h>
#include <clocale> // std::lconv
#include <cstdio>
#include <cstdlib>
@@ -20,6 +19,7 @@
#include <stdarg.h>
#include <string.h>
#include <time.h>
+#include <wchar.h>
#if _LIBCPP_HAS_WIDE_CHARACTERS
# include <cwchar>
# include <wctype.h>
diff --git a/libcxx/include/__locale_dir/support/no_locale/conversions.h b/libcxx/include/__locale_dir/support/no_locale/conversions.h
index a9495e2b8b1ec..ec3fd48d1b45c 100644
--- a/libcxx/include/__locale_dir/support/no_locale/conversions.h
+++ b/libcxx/include/__locale_dir/support/no_locale/conversions.h
@@ -11,8 +11,8 @@
#include <__config>
#include <__cstddef/size_t.h>
-#include <wchar.h>
#include <cstdlib>
+#include <wchar.h>
#if _LIBCPP_HAS_WIDE_CHARACTERS
# include <cwchar>
#endif
diff --git a/libcxx/include/__locale_dir/support/openbsd.h b/libcxx/include/__locale_dir/support/openbsd.h
index 157c230b767cb..6fa2062f6967b 100644
--- a/libcxx/include/__locale_dir/support/openbsd.h
+++ b/libcxx/include/__locale_dir/support/openbsd.h
@@ -12,7 +12,6 @@
#include <__config>
#include <__cstddef/size_t.h>
-#include <wchar.h>
#include <__utility/forward.h>
#include <clocale> // std::lconv
#include <ctype.h>
@@ -20,6 +19,7 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#include <wchar.h>
#if _LIBCPP_HAS_WIDE_CHARACTERS
# include <wchar.h>
# include <wctype.h>
diff --git a/libcxx/include/__locale_dir/utf8_conversions.h b/libcxx/include/__locale_dir/utf8_conversions.h
index 68d435488f64d..563a46f5e3b58 100644
--- a/libcxx/include/__locale_dir/utf8_conversions.h
+++ b/libcxx/include/__locale_dir/utf8_conversions.h
@@ -15,8 +15,8 @@
# include <__cstddef/size_t.h>
# include <__locale_dir/codecvt.h>
-# include <wchar.h>
# include <stdexcept>
+# include <wchar.h>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
diff --git a/libcxx/include/__string/char_traits.h b/libcxx/include/__string/char_traits.h
index 927d89cc5ce90..714a0193731a6 100644
--- a/libcxx/include/__string/char_traits.h
+++ b/libcxx/include/__string/char_traits.h
@@ -21,13 +21,13 @@
#include <__functional/hash.h>
#include <__functional/identity.h>
#include <__iterator/iterator_traits.h>
-#include <wchar.h>
#include <__string/constexpr_c_functions.h>
#include <__type_traits/is_constant_evaluated.h>
#include <__utility/is_pointer_in_range.h>
#include <cstdint>
#include <cstdio>
#include <iosfwd>
+#include <wchar.h>
#if _LIBCPP_HAS_WIDE_CHARACTERS
# include <cwchar> // for wmemcpy
diff --git a/libcxx/include/iosfwd b/libcxx/include/iosfwd
index 492c5a478770c..11241afd68e53 100644
--- a/libcxx/include/iosfwd
+++ b/libcxx/include/iosfwd
@@ -117,8 +117,8 @@ using wosyncstream = basic_osyncstream<wchar_t>; // C++20
# include <__fwd/sstream.h>
# include <__fwd/streambuf.h>
# include <__fwd/string.h>
-# include <wchar.h>
# include <version>
+# include <wchar.h>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
>From 51a71c71b1c3ecb072316251790898aad669bc9f Mon Sep 17 00:00:00 2001
From: Zibi Sarbinowski <zibi at ca.ibm.com>
Date: Thu, 20 Aug 2026 13:48:37 -0400
Subject: [PATCH 4/4] [libc++] Add __fwd/mbstate_t.h to fix mbstate_t
visibility in modules build
The previous commit replaced <__std_mbstate_t.h> (which had its own module
entry) with direct <wchar.h> includes. Since <wchar.h> is declared as a
textual header in module.modulemap.in, mbstate_t was no longer visible
through the module system, causing a failure in the generic-modules build:
declaration of 'mbstate_t' must be imported from module 'std.cuchar'
before it is required
Fix by introducing __fwd/mbstate_t.h, a proper modularized header that
includes <wchar.h> and pulls ::mbstate_t into namespace std, mirroring
the pattern of other __fwd/ headers. Register it as a submodule of iosfwd
in module.modulemap.in and list it in CMakeLists.txt.
Use <__fwd/mbstate_t.h> in iosfwd and __string/char_traits.h (the two
headers reachable from the modules build path) in place of <wchar.h>.
---
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__fwd/mbstate_t.h | 26 ++++++++++++++++++++++++++
libcxx/include/__string/char_traits.h | 2 +-
libcxx/include/iosfwd | 2 +-
libcxx/include/module.modulemap.in | 1 +
5 files changed, 30 insertions(+), 2 deletions(-)
create mode 100644 libcxx/include/__fwd/mbstate_t.h
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 026dcdcc513af..9e80857f6df3a 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -471,6 +471,7 @@ set(files
__fwd/get.h
__fwd/ios.h
__fwd/istream.h
+ __fwd/mbstate_t.h
__fwd/mdspan.h
__fwd/memory.h
__fwd/memory_resource.h
diff --git a/libcxx/include/__fwd/mbstate_t.h b/libcxx/include/__fwd/mbstate_t.h
new file mode 100644
index 0000000000000..59af78e10a001
--- /dev/null
+++ b/libcxx/include/__fwd/mbstate_t.h
@@ -0,0 +1,26 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___FWD_MBSTATE_T_H
+#define _LIBCPP___FWD_MBSTATE_T_H
+
+#include <__config>
+#include <wchar.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+using ::mbstate_t _LIBCPP_USING_IF_EXISTS;
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___FWD_MBSTATE_T_H
diff --git a/libcxx/include/__string/char_traits.h b/libcxx/include/__string/char_traits.h
index 714a0193731a6..10aa152ad48fd 100644
--- a/libcxx/include/__string/char_traits.h
+++ b/libcxx/include/__string/char_traits.h
@@ -18,6 +18,7 @@
#include <__compare/ordering.h>
#include <__config>
#include <__cstddef/ptrdiff_t.h>
+#include <__fwd/mbstate_t.h>
#include <__functional/hash.h>
#include <__functional/identity.h>
#include <__iterator/iterator_traits.h>
@@ -27,7 +28,6 @@
#include <cstdint>
#include <cstdio>
#include <iosfwd>
-#include <wchar.h>
#if _LIBCPP_HAS_WIDE_CHARACTERS
# include <cwchar> // for wmemcpy
diff --git a/libcxx/include/iosfwd b/libcxx/include/iosfwd
index 11241afd68e53..4d9f33143fde6 100644
--- a/libcxx/include/iosfwd
+++ b/libcxx/include/iosfwd
@@ -112,13 +112,13 @@ using wosyncstream = basic_osyncstream<wchar_t>; // C++20
# include <__fwd/fstream.h>
# include <__fwd/ios.h>
# include <__fwd/istream.h>
+# include <__fwd/mbstate_t.h>
# include <__fwd/memory.h>
# include <__fwd/ostream.h>
# include <__fwd/sstream.h>
# include <__fwd/streambuf.h>
# include <__fwd/string.h>
# include <version>
-# include <wchar.h>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 46b45cf75ccdb..b972ff10039ff 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1501,6 +1501,7 @@ module std {
}
module iosfwd {
+ module fwd { header "__fwd/mbstate_t.h" }
header "iosfwd"
export *
}
More information about the libcxx-commits
mailing list