[libcxx-commits] [libcxx] 12f5729 - [libc++][windows] Use _wsetlocale() in __locale_guard (#160479)

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Nov 2 12:47:00 PST 2026


Author: lb90
Date: 2026-11-02T22:46:55+02:00
New Revision: 12f572979922ca10b9c66d6264826f9cbc78f5de

URL: https://github.com/llvm/llvm-project/commit/12f572979922ca10b9c66d6264826f9cbc78f5de
DIFF: https://github.com/llvm/llvm-project/commit/12f572979922ca10b9c66d6264826f9cbc78f5de.diff

LOG: [libc++][windows] Use _wsetlocale() in __locale_guard (#160479)

On Windows, locale strings are not limited to ASCII characters, and
narrow locale strings are encoded relative to the `LC_CTYPE` charset.

Class `__locale_guard` does three things in sequence:

1. Queries the current locale string
2. Sets a temporary locale
3. Restores the locale retrieved at 1

However, the locale string obtained from `1)` may be in an encoding
different from what `setlocale` expects at `3)`

To avoid any problem with narrow string encodings, use `_wsetlocale` in
`__locale_guard`.

Fixes #160478

Added: 
    libcxx/test/libcxx/localization/locales/windows_non_ascii_locale_names.pass.cpp

Modified: 
    libcxx/include/__cxx03/__locale_dir/locale_base_api/locale_guard.h
    libcxx/include/__locale_dir/support/windows.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__cxx03/__locale_dir/locale_base_api/locale_guard.h b/libcxx/include/__cxx03/__locale_dir/locale_base_api/locale_guard.h
index e3583634e4322b..7065874ea6d326 100644
--- a/libcxx/include/__cxx03/__locale_dir/locale_base_api/locale_guard.h
+++ b/libcxx/include/__cxx03/__locale_dir/locale_base_api/locale_guard.h
@@ -45,7 +45,11 @@ struct __libcpp_locale_guard {
     // each category.  In the second case, we know at least one category won't
     // be what we want, so we only have to check the first case.
     if (std::strcmp(__l.__get_locale(), __lc) != 0) {
-      __locale_all = _strdup(__lc);
+      // Use wsetlocale to query the current locale string. This avoids a lossy
+      // conversion of the locale string from UTF-16 to the current LC_CTYPE
+      // charset. The Windows CRT allows language / country strings outside of
+      // ASCII, e.g. "Norwegian Bokm\u00E5l_Norway.utf8".
+      __locale_all = _wcsdup(__wsetlocale(nullptr));
       if (__locale_all == nullptr)
         __throw_bad_alloc();
       __setlocale(__l.__get_locale());
@@ -57,7 +61,7 @@ struct __libcpp_locale_guard {
     // for the 
diff erent categories in the same format as returned by
     // setlocale(LC_ALL, nullptr).
     if (__locale_all != nullptr) {
-      __setlocale(__locale_all);
+      __wsetlocale(__locale_all);
       free(__locale_all);
     }
     _configthreadlocale(__status);
@@ -68,8 +72,14 @@ struct __libcpp_locale_guard {
       __throw_bad_alloc();
     return __new_locale;
   }
+  static const wchar_t* __wsetlocale(const wchar_t* __locale) {
+    const wchar_t* __new_locale = _wsetlocale(LC_ALL, __locale);
+    if (__new_locale == nullptr)
+      __throw_bad_alloc();
+    return __new_locale;
+  }
   int __status;
-  char* __locale_all = nullptr;
+  wchar_t* __locale_all = nullptr;
 };
 #endif
 

diff  --git a/libcxx/include/__locale_dir/support/windows.h b/libcxx/include/__locale_dir/support/windows.h
index a72aaccccef159..0bbc96aaebffcc 100644
--- a/libcxx/include/__locale_dir/support/windows.h
+++ b/libcxx/include/__locale_dir/support/windows.h
@@ -166,6 +166,12 @@ inline _LIBCPP_HIDE_FROM_ABI char* __setlocale(int __category, const char* __loc
     std::__throw_bad_alloc();
   return __new_locale;
 }
+inline _LIBCPP_HIDE_FROM_ABI wchar_t* __wsetlocale(int __category, const wchar_t* __locale) {
+  wchar_t* __new_locale = ::_wsetlocale(__category, __locale);
+  if (__new_locale == nullptr)
+    std::__throw_bad_alloc();
+  return __new_locale;
+}
 _LIBCPP_EXPORTED_FROM_ABI __lconv_t* __localeconv(__locale_t& __loc);
 _LIBCPP_EXPORTED_FROM_ABI const char* __get_locale_encoding(__locale_t __loc);
 #endif // _LIBCPP_BUILDING_LIBRARY
@@ -299,7 +305,11 @@ struct __locale_guard {
     // each category.  In the second case, we know at least one category won't
     // be what we want, so we only have to check the first case.
     if (std::strcmp(__l.__get_locale(), __lc) != 0) {
-      __locale_all = _strdup(__lc);
+      // Use wsetlocale to query the current locale string. This avoids a lossy
+      // conversion of the locale string from UTF-16 to the current LC_CTYPE
+      // charset. The Windows CRT allows language / country strings outside of
+      // ASCII, e.g. "Norwegian Bokm\u00E5l_Norway.utf8".
+      __locale_all = _wcsdup(__locale::__wsetlocale(LC_ALL, nullptr));
       if (__locale_all == nullptr)
         std::__throw_bad_alloc();
       __locale::__setlocale(LC_ALL, __l.__get_locale());
@@ -311,13 +321,13 @@ struct __locale_guard {
     // for the 
diff erent categories in the same format as returned by
     // setlocale(LC_ALL, nullptr).
     if (__locale_all != nullptr) {
-      __locale::__setlocale(LC_ALL, __locale_all);
+      __locale::__wsetlocale(LC_ALL, __locale_all);
       free(__locale_all);
     }
     _configthreadlocale(__status);
   }
   int __status;
-  char* __locale_all = nullptr;
+  wchar_t* __locale_all = nullptr;
 };
 #endif // _LIBCPP_BUILDING_LIBRARY
 

diff  --git a/libcxx/test/libcxx/localization/locales/windows_non_ascii_locale_names.pass.cpp b/libcxx/test/libcxx/localization/locales/windows_non_ascii_locale_names.pass.cpp
new file mode 100644
index 00000000000000..b4b7934986d99c
--- /dev/null
+++ b/libcxx/test/libcxx/localization/locales/windows_non_ascii_locale_names.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <locale>
+
+// REQUIRES: windows
+
+// The C RunTime library on Windows supports locale strings with
+// characters outside the ASCII range. This poses challenges for
+// code that temporarily set a custom thread locale.
+//
+// https://github.com/llvm/llvm-project/issues/160478
+
+#include <locale>
+#include <iostream>
+#include <iomanip>
+#include <algorithm>
+
+#include <cstdlib>
+#include <cassert>
+#include <clocale>
+
+#include "test_macros.h"
+
+void locale_name_replace_codepage(std::string& locale_name, const std::string& codepage) {
+  auto dot_position = locale_name.rfind('.');
+  LIBCPP_ASSERT(dot_position != std::string::npos);
+
+  locale_name = locale_name.substr(0, dot_position) + codepage;
+}
+
+int main(int, char**) {
+  _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+
+  std::string locale_name = std::setlocale(LC_ALL, "norwegian-bokmal");
+
+  const auto& not_ascii = [](char c) { return (c & 0x80) != 0; };
+  LIBCPP_ASSERT(std::any_of(locale_name.begin(), locale_name.end(), not_ascii));
+
+  locale_name_replace_codepage(locale_name, ".437");
+  LIBCPP_ASSERT(std::setlocale(LC_ALL, locale_name.c_str()));
+
+  std::cerr.imbue(std::locale::classic());
+  std::cerr << std::setprecision(2) << 0.1 << std::endl;
+
+  return EXIT_SUCCESS;
+}


        


More information about the libcxx-commits mailing list