[libcxx-commits] [libcxx] [libcxx] Fix ODR violation in wchar.h (PR #216069)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Aug 14 07:32:55 PDT 2026


https://github.com/aokblast updated https://github.com/llvm/llvm-project/pull/216069

>From 164de49e3b7ab9fc2924c4808dd6d23d12e7aadd Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Thu, 13 Aug 2026 22:28:21 +0800
Subject: [PATCH 1/2] [libcxx] Fix ODR violation in FreeBSD path

When building stdcxx clang module, it include cwchar and trigger
inclusion of wchar.h in libcxx, which expand inline function of
__libcpp_wcschr. However, wchar.h includes __mbstate_t, which include
wchar.h again. This causes __libcpp_wcschr exposes to
std_private_mbstate_t and create ODR violation.

It doesn't happen on other platforms as we don't instantiate
__libcpp_wcschr as _LIBCPP_WCHAR_H_HAS_CONST_OVERLOADS is defined.

To fix it, we creates a stub macro _LIBCPP_WCHAR_H_SYSTEM_HEADER_ONLY to
make wchar.h instantiation is not happens when building
std_private_mbstate_t module.
---
 libcxx/include/__mbstate_t.h                              | 7 +++++++
 libcxx/include/wchar.h                                    | 5 ++++-
 libcxx/test/extensions/clang/clang_modules_include.gen.py | 3 ---
 3 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 1ce5f0cd6da1d..5a371a3d889e9 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -48,7 +48,14 @@
 #elif __has_include(<bits/mbstate_t.h>)
 #  include <bits/mbstate_t.h> // works for Android
 #elif __has_include_next(<wchar.h>)
+// When this header is built as its own Clang module, the #include_next below can resolve back to
+// // libc++'s own <wchar.h> instead of the C library's one, since the lookup starts over from the
+// // beginning of the search path. libc++'s <wchar.h> forwards to the C library's one, but it would
+// // also emit its inline wcschr() & friends overloads into this module, in addition to the module
+// // owning <cwchar>. Tell it to provide only the C library declarations.
+#  define _LIBCPP_WCHAR_H_SYSTEM_HEADER_ONLY
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
+#  undef _LIBCPP_WCHAR_H_SYSTEM_HEADER_ONLY
 #elif __has_include_next(<uchar.h>)
 #  include_next <uchar.h> // Try <uchar.h> in absence of <wchar.h> for mbstate_t
 #else
diff --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index e5844249bdf7e..e68e0f64e8491 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -116,7 +116,10 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
 #    include_next <wchar.h>
 #  endif
 
-#  ifndef _LIBCPP_WCHAR_H
+// <__mbstate_t.h> can reach us through its #include_next of <wchar.h>. It only needs the C library
+// // declarations pulled in above, and including <__mbstate_t.h> from here would be cyclic, so stop
+// // before libc++'s own additions in that case.
+#  if !defined(_LIBCPP_WCHAR_H) && !defined(_LIBCPP_WCHAR_H_SYSTEM_HEADER_ONLY)
 #    define _LIBCPP_WCHAR_H
 
 #    include <__mbstate_t.h> // provide mbstate_t
diff --git a/libcxx/test/extensions/clang/clang_modules_include.gen.py b/libcxx/test/extensions/clang/clang_modules_include.gen.py
index ed058cd908511..7ec53f5f8115f 100644
--- a/libcxx/test/extensions/clang/clang_modules_include.gen.py
+++ b/libcxx/test/extensions/clang/clang_modules_include.gen.py
@@ -24,9 +24,6 @@
 # The Android headers don't appear to be compatible with modules yet
 # UNSUPPORTED: LIBCXX-ANDROID-FIXME
 
-# TODO: Investigate this failure
-# UNSUPPORTED: LIBCXX-FREEBSD-FIXME
-
 # TODO: Investigate why this doesn't work on Picolibc once the locale base API is refactored
 # UNSUPPORTED: LIBCXX-PICOLIBC-FIXME
 

>From 60700062fdee32615849e97d7efd15a1b54ec208 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Fri, 14 Aug 2026 22:32:39 +0800
Subject: [PATCH 2/2] fixup! [libcxx] Fix ODR violation in FreeBSD path

---
 libcxx/include/__mbstate_t.h | 8 ++++----
 libcxx/include/wchar.h       | 4 ++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/__mbstate_t.h b/libcxx/include/__mbstate_t.h
index 5a371a3d889e9..7bbeb5a271899 100644
--- a/libcxx/include/__mbstate_t.h
+++ b/libcxx/include/__mbstate_t.h
@@ -49,10 +49,10 @@
 #  include <bits/mbstate_t.h> // works for Android
 #elif __has_include_next(<wchar.h>)
 // When this header is built as its own Clang module, the #include_next below can resolve back to
-// // libc++'s own <wchar.h> instead of the C library's one, since the lookup starts over from the
-// // beginning of the search path. libc++'s <wchar.h> forwards to the C library's one, but it would
-// // also emit its inline wcschr() & friends overloads into this module, in addition to the module
-// // owning <cwchar>. Tell it to provide only the C library declarations.
+// libc++'s own <wchar.h> instead of the C library's one, since the lookup starts over from the
+// beginning of the search path. libc++'s <wchar.h> forwards to the C library's one, but it would
+// also emit its inline wcschr() & friends overloads into this module, in addition to the module
+// owning <cwchar>. Tell it to provide only the C library declarations.
 #  define _LIBCPP_WCHAR_H_SYSTEM_HEADER_ONLY
 #  include_next <wchar.h> // use the C standard provider of mbstate_t if present
 #  undef _LIBCPP_WCHAR_H_SYSTEM_HEADER_ONLY
diff --git a/libcxx/include/wchar.h b/libcxx/include/wchar.h
index e68e0f64e8491..b54e404e2c721 100644
--- a/libcxx/include/wchar.h
+++ b/libcxx/include/wchar.h
@@ -117,8 +117,8 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
 #  endif
 
 // <__mbstate_t.h> can reach us through its #include_next of <wchar.h>. It only needs the C library
-// // declarations pulled in above, and including <__mbstate_t.h> from here would be cyclic, so stop
-// // before libc++'s own additions in that case.
+// declarations pulled in above, and including <__mbstate_t.h> from here would be cyclic, so stop
+// before libc++'s own additions in that case.
 #  if !defined(_LIBCPP_WCHAR_H) && !defined(_LIBCPP_WCHAR_H_SYSTEM_HEADER_ONLY)
 #    define _LIBCPP_WCHAR_H
 



More information about the libcxx-commits mailing list