[libcxx-commits] [libcxx] [libc++] Refactor the Windows and MinGW implementation of the locale base API (PR #115752)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Thu Nov 28 13:45:26 PST 2024


https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/115752

>From 767e60c05459c685289e32bed13d559b86c76d1d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 31 Oct 2024 10:23:21 -0400
Subject: [PATCH] [libc++] Refactor the Windows and MinGW implementation of the
 locale base API

This patch reimplements the locale base support for Windows flavors
in a way that is more modules-friendly and without defining non-internal
names.

Since this changes the name of some types and entry points in the
built library, this is effectively an ABI break on Windows (which
is acceptable since we don't promise ABI stability on that platform).
---
 libcxx/docs/ReleaseNotes/20.rst               |   3 +
 libcxx/include/CMakeLists.txt                 |   2 +-
 libcxx/include/__locale_dir/locale_base_api.h |   6 +-
 .../__locale_dir/locale_base_api/win32.h      | 235 --------------
 libcxx/include/__locale_dir/support/windows.h | 293 ++++++++++++++++++
 libcxx/include/module.modulemap               |   2 +-
 libcxx/src/support/win32/locale_win32.cpp     | 183 +++++++----
 libcxx/src/support/win32/support.cpp          |  33 --
 8 files changed, 416 insertions(+), 341 deletions(-)
 delete mode 100644 libcxx/include/__locale_dir/locale_base_api/win32.h
 create mode 100644 libcxx/include/__locale_dir/support/windows.h

diff --git a/libcxx/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst
index a72fd2d5db9e26..c8a07fb8b73348 100644
--- a/libcxx/docs/ReleaseNotes/20.rst
+++ b/libcxx/docs/ReleaseNotes/20.rst
@@ -152,6 +152,9 @@ ABI Affecting Changes
   - When using the MSVC ABI, this change results in some classes having a completely different memory layout, so this is
     a genuine ABI break. However, the library does not currently guarantee ABI stability on MSVC platforms.
 
+- The localization support base API has been reimplemented, leading to different functions being exported from the
+  libc++ built library on Windows and Windows-like platforms.
+
 Build System Changes
 --------------------
 
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 2bb6d263340d30..c7265439d1a6f4 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -500,12 +500,12 @@ set(files
   __locale_dir/locale_base_api/ibm.h
   __locale_dir/locale_base_api/musl.h
   __locale_dir/locale_base_api/openbsd.h
-  __locale_dir/locale_base_api/win32.h
   __locale_dir/locale_guard.h
   __locale_dir/pad_and_output.h
   __locale_dir/support/apple.h
   __locale_dir/support/bsd_like.h
   __locale_dir/support/freebsd.h
+  __locale_dir/support/windows.h
   __math/abs.h
   __math/copysign.h
   __math/error_functions.h
diff --git a/libcxx/include/__locale_dir/locale_base_api.h b/libcxx/include/__locale_dir/locale_base_api.h
index 8ed4c29cb8732f..7e3c205c04a780 100644
--- a/libcxx/include/__locale_dir/locale_base_api.h
+++ b/libcxx/include/__locale_dir/locale_base_api.h
@@ -98,15 +98,15 @@
 #  include <__locale_dir/support/apple.h>
 #elif defined(__FreeBSD__)
 #  include <__locale_dir/support/freebsd.h>
+#elif defined(_LIBCPP_MSVCRT_LIKE)
+#  include <__locale_dir/support/windows.h>
 #else
 
 // TODO: This is a temporary definition to bridge between the old way we defined the locale base API
 //       (by providing global non-reserved names) and the new API. As we move individual platforms
 //       towards the new way of defining the locale base API, this should disappear since each platform
 //       will define those directly.
-#  if defined(_LIBCPP_MSVCRT_LIKE)
-#    include <__locale_dir/locale_base_api/win32.h>
-#  elif defined(_AIX) || defined(__MVS__)
+#  if defined(_AIX) || defined(__MVS__)
 #    include <__locale_dir/locale_base_api/ibm.h>
 #  elif defined(__ANDROID__)
 #    include <__locale_dir/locale_base_api/android.h>
diff --git a/libcxx/include/__locale_dir/locale_base_api/win32.h b/libcxx/include/__locale_dir/locale_base_api/win32.h
deleted file mode 100644
index f488a0dc0d69b3..00000000000000
--- a/libcxx/include/__locale_dir/locale_base_api/win32.h
+++ /dev/null
@@ -1,235 +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___LOCALE_DIR_LOCALE_BASE_API_WIN32_H
-#define _LIBCPP___LOCALE_DIR_LOCALE_BASE_API_WIN32_H
-
-#include <__config>
-#include <cstddef>
-#include <locale.h> // _locale_t
-#include <stdio.h>
-#include <string>
-
-#define _X_ALL LC_ALL
-#define _X_COLLATE LC_COLLATE
-#define _X_CTYPE LC_CTYPE
-#define _X_MONETARY LC_MONETARY
-#define _X_NUMERIC LC_NUMERIC
-#define _X_TIME LC_TIME
-#define _X_MAX LC_MAX
-#define _X_MESSAGES 6
-#define _NCAT (_X_MESSAGES + 1)
-
-#define _CATMASK(n) ((1 << (n)) >> 1)
-#define _M_COLLATE _CATMASK(_X_COLLATE)
-#define _M_CTYPE _CATMASK(_X_CTYPE)
-#define _M_MONETARY _CATMASK(_X_MONETARY)
-#define _M_NUMERIC _CATMASK(_X_NUMERIC)
-#define _M_TIME _CATMASK(_X_TIME)
-#define _M_MESSAGES _CATMASK(_X_MESSAGES)
-#define _M_ALL (_CATMASK(_NCAT) - 1)
-
-#define LC_COLLATE_MASK _M_COLLATE
-#define LC_CTYPE_MASK _M_CTYPE
-#define LC_MONETARY_MASK _M_MONETARY
-#define LC_NUMERIC_MASK _M_NUMERIC
-#define LC_TIME_MASK _M_TIME
-#define LC_MESSAGES_MASK _M_MESSAGES
-#define LC_ALL_MASK                                                                                                    \
-  (LC_COLLATE_MASK | LC_CTYPE_MASK | LC_MESSAGES_MASK | LC_MONETARY_MASK | LC_NUMERIC_MASK | LC_TIME_MASK)
-
-class __lconv_storage {
-public:
-  __lconv_storage(const lconv* __lc_input) {
-    __lc_ = *__lc_input;
-
-    __decimal_point_     = __lc_input->decimal_point;
-    __thousands_sep_     = __lc_input->thousands_sep;
-    __grouping_          = __lc_input->grouping;
-    __int_curr_symbol_   = __lc_input->int_curr_symbol;
-    __currency_symbol_   = __lc_input->currency_symbol;
-    __mon_decimal_point_ = __lc_input->mon_decimal_point;
-    __mon_thousands_sep_ = __lc_input->mon_thousands_sep;
-    __mon_grouping_      = __lc_input->mon_grouping;
-    __positive_sign_     = __lc_input->positive_sign;
-    __negative_sign_     = __lc_input->negative_sign;
-
-    __lc_.decimal_point     = const_cast<char*>(__decimal_point_.c_str());
-    __lc_.thousands_sep     = const_cast<char*>(__thousands_sep_.c_str());
-    __lc_.grouping          = const_cast<char*>(__grouping_.c_str());
-    __lc_.int_curr_symbol   = const_cast<char*>(__int_curr_symbol_.c_str());
-    __lc_.currency_symbol   = const_cast<char*>(__currency_symbol_.c_str());
-    __lc_.mon_decimal_point = const_cast<char*>(__mon_decimal_point_.c_str());
-    __lc_.mon_thousands_sep = const_cast<char*>(__mon_thousands_sep_.c_str());
-    __lc_.mon_grouping      = const_cast<char*>(__mon_grouping_.c_str());
-    __lc_.positive_sign     = const_cast<char*>(__positive_sign_.c_str());
-    __lc_.negative_sign     = const_cast<char*>(__negative_sign_.c_str());
-  }
-
-  lconv* __get() { return &__lc_; }
-
-private:
-  lconv __lc_;
-  std::string __decimal_point_;
-  std::string __thousands_sep_;
-  std::string __grouping_;
-  std::string __int_curr_symbol_;
-  std::string __currency_symbol_;
-  std::string __mon_decimal_point_;
-  std::string __mon_thousands_sep_;
-  std::string __mon_grouping_;
-  std::string __positive_sign_;
-  std::string __negative_sign_;
-};
-
-class locale_t {
-public:
-  locale_t() : __locale_(nullptr), __locale_str_(nullptr), __lc_(nullptr) {}
-  locale_t(std::nullptr_t) : __locale_(nullptr), __locale_str_(nullptr), __lc_(nullptr) {}
-  locale_t(_locale_t __xlocale, const char* __xlocale_str)
-      : __locale_(__xlocale), __locale_str_(__xlocale_str), __lc_(nullptr) {}
-  locale_t(const locale_t& __l) : __locale_(__l.__locale_), __locale_str_(__l.__locale_str_), __lc_(nullptr) {}
-
-  ~locale_t() { delete __lc_; }
-
-  locale_t& operator=(const locale_t& __l) {
-    __locale_     = __l.__locale_;
-    __locale_str_ = __l.__locale_str_;
-    // __lc_ not copied
-    return *this;
-  }
-
-  friend bool operator==(const locale_t& __left, const locale_t& __right) {
-    return __left.__locale_ == __right.__locale_;
-  }
-
-  friend bool operator==(const locale_t& __left, int __right) { return __left.__locale_ == nullptr && __right == 0; }
-
-  friend bool operator==(const locale_t& __left, long long __right) {
-    return __left.__locale_ == nullptr && __right == 0;
-  }
-
-  friend bool operator==(const locale_t& __left, std::nullptr_t) { return __left.__locale_ == nullptr; }
-
-  friend bool operator==(int __left, const locale_t& __right) { return __left == 0 && nullptr == __right.__locale_; }
-
-  friend bool operator==(std::nullptr_t, const locale_t& __right) { return nullptr == __right.__locale_; }
-
-  friend bool operator!=(const locale_t& __left, const locale_t& __right) { return !(__left == __right); }
-
-  friend bool operator!=(const locale_t& __left, int __right) { return !(__left == __right); }
-
-  friend bool operator!=(const locale_t& __left, long long __right) { return !(__left == __right); }
-
-  friend bool operator!=(const locale_t& __left, std::nullptr_t __right) { return !(__left == __right); }
-
-  friend bool operator!=(int __left, const locale_t& __right) { return !(__left == __right); }
-
-  friend bool operator!=(std::nullptr_t __left, const locale_t& __right) { return !(__left == __right); }
-
-  operator bool() const { return __locale_ != nullptr; }
-
-  const char* __get_locale() const { return __locale_str_; }
-
-  operator _locale_t() const { return __locale_; }
-
-  lconv* __store_lconv(const lconv* __input_lc) {
-    delete __lc_;
-    __lc_ = new __lconv_storage(__input_lc);
-    return __lc_->__get();
-  }
-
-private:
-  _locale_t __locale_;
-  const char* __locale_str_;
-  __lconv_storage* __lc_ = nullptr;
-};
-
-// Locale management functions
-#define freelocale _free_locale
-// FIXME: base currently unused. Needs manual work to construct the new locale
-locale_t newlocale(int __mask, const char* __locale, locale_t __base);
-// uselocale can't be implemented on Windows because Windows allows partial modification
-// of thread-local locale and so _get_current_locale() returns a copy while uselocale does
-// not create any copies.
-// We can still implement raii even without uselocale though.
-
-lconv* localeconv_l(locale_t& __loc);
-size_t mbrlen_l(const char* __restrict __s, size_t __n, mbstate_t* __restrict __ps, locale_t __loc);
-size_t mbsrtowcs_l(
-    wchar_t* __restrict __dst, const char** __restrict __src, size_t __len, mbstate_t* __restrict __ps, locale_t __loc);
-size_t wcrtomb_l(char* __restrict __s, wchar_t __wc, mbstate_t* __restrict __ps, locale_t __loc);
-size_t mbrtowc_l(
-    wchar_t* __restrict __pwc, const char* __restrict __s, size_t __n, mbstate_t* __restrict __ps, locale_t __loc);
-size_t mbsnrtowcs_l(wchar_t* __restrict __dst,
-                    const char** __restrict __src,
-                    size_t __nms,
-                    size_t __len,
-                    mbstate_t* __restrict __ps,
-                    locale_t __loc);
-size_t wcsnrtombs_l(char* __restrict __dst,
-                    const wchar_t** __restrict __src,
-                    size_t __nwc,
-                    size_t __len,
-                    mbstate_t* __restrict __ps,
-                    locale_t __loc);
-wint_t btowc_l(int __c, locale_t __loc);
-int wctob_l(wint_t __c, locale_t __loc);
-
-decltype(MB_CUR_MAX) MB_CUR_MAX_L(locale_t __l);
-
-// the *_l functions are prefixed on Windows, only available for msvcr80+, VS2005+
-#define mbtowc_l _mbtowc_l
-#define strtoll_l _strtoi64_l
-#define strtoull_l _strtoui64_l
-#define strtod_l _strtod_l
-#if defined(_LIBCPP_MSVCRT)
-#  define strtof_l _strtof_l
-#  define strtold_l _strtold_l
-#else
-_LIBCPP_EXPORTED_FROM_ABI float strtof_l(const char*, char**, locale_t);
-_LIBCPP_EXPORTED_FROM_ABI long double strtold_l(const char*, char**, locale_t);
-#endif
-inline _LIBCPP_HIDE_FROM_ABI int islower_l(int __c, _locale_t __loc) { return _islower_l((int)__c, __loc); }
-
-inline _LIBCPP_HIDE_FROM_ABI int isupper_l(int __c, _locale_t __loc) { return _isupper_l((int)__c, __loc); }
-
-#define isdigit_l _isdigit_l
-#define isxdigit_l _isxdigit_l
-#define strcoll_l _strcoll_l
-#define strxfrm_l _strxfrm_l
-#define wcscoll_l _wcscoll_l
-#define wcsxfrm_l _wcsxfrm_l
-#define toupper_l _toupper_l
-#define tolower_l _tolower_l
-#define iswspace_l _iswspace_l
-#define iswprint_l _iswprint_l
-#define iswcntrl_l _iswcntrl_l
-#define iswupper_l _iswupper_l
-#define iswlower_l _iswlower_l
-#define iswalpha_l _iswalpha_l
-#define iswdigit_l _iswdigit_l
-#define iswpunct_l _iswpunct_l
-#define iswxdigit_l _iswxdigit_l
-#define towupper_l _towupper_l
-#define towlower_l _towlower_l
-#if defined(__MINGW32__) && __MSVCRT_VERSION__ < 0x0800
-_LIBCPP_EXPORTED_FROM_ABI size_t strftime_l(char* ret, size_t n, const char* format, const struct tm* tm, locale_t loc);
-#else
-#  define strftime_l _strftime_l
-#endif
-#define sscanf_l(__s, __l, __f, ...) _sscanf_l(__s, __f, __l, __VA_ARGS__)
-_LIBCPP_EXPORTED_FROM_ABI int snprintf_l(char* __ret, size_t __n, locale_t __loc, const char* __format, ...);
-_LIBCPP_EXPORTED_FROM_ABI int asprintf_l(char** __ret, locale_t __loc, const char* __format, ...);
-_LIBCPP_EXPORTED_FROM_ABI int vasprintf_l(char** __ret, locale_t __loc, const char* __format, va_list __ap);
-
-// not-so-pressing FIXME: use locale to determine blank characters
-inline int iswblank_l(wint_t __c, locale_t /*loc*/) { return (__c == L' ' || __c == L'\t'); }
-
-#endif // _LIBCPP___LOCALE_DIR_LOCALE_BASE_API_WIN32_H
diff --git a/libcxx/include/__locale_dir/support/windows.h b/libcxx/include/__locale_dir/support/windows.h
new file mode 100644
index 00000000000000..b6d67abff26882
--- /dev/null
+++ b/libcxx/include/__locale_dir/support/windows.h
@@ -0,0 +1,293 @@
+//===-----------------------------------------------------------------------===//
+//
+// 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___LOCALE_DIR_SUPPORT_WINDOWS_H
+#define _LIBCPP___LOCALE_DIR_SUPPORT_WINDOWS_H
+
+#include <__config>
+#include <__cstddef/nullptr_t.h>
+#include <__utility/forward.h>
+#include <clocale> // std::lconv & friends
+#include <cstddef>
+#include <ctype.h>  // ::_isupper_l & friends
+#include <locale.h> // ::_locale_t
+#include <stdio.h>  // ::_sscanf_l
+#include <stdlib.h> // ::_strtod_l & friends
+#include <string.h> // ::_strcoll_l
+#include <string>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#define _CATMASK(n) ((1 << (n)) >> 1)
+#define LC_COLLATE_MASK _CATMASK(LC_COLLATE)
+#define LC_CTYPE_MASK _CATMASK(LC_CTYPE)
+#define LC_MONETARY_MASK _CATMASK(LC_MONETARY)
+#define LC_NUMERIC_MASK _CATMASK(LC_NUMERIC)
+#define LC_TIME_MASK _CATMASK(LC_TIME)
+#define LC_MESSAGES_MASK _CATMASK(6)
+#define LC_ALL_MASK                                                                                                    \
+  (LC_COLLATE_MASK | LC_CTYPE_MASK | LC_MESSAGES_MASK | LC_MONETARY_MASK | LC_NUMERIC_MASK | LC_TIME_MASK)
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __locale {
+
+class __lconv_storage {
+public:
+  __lconv_storage(const lconv* __lc_input) {
+    __lc_ = *__lc_input;
+
+    __decimal_point_     = __lc_input->decimal_point;
+    __thousands_sep_     = __lc_input->thousands_sep;
+    __grouping_          = __lc_input->grouping;
+    __int_curr_symbol_   = __lc_input->int_curr_symbol;
+    __currency_symbol_   = __lc_input->currency_symbol;
+    __mon_decimal_point_ = __lc_input->mon_decimal_point;
+    __mon_thousands_sep_ = __lc_input->mon_thousands_sep;
+    __mon_grouping_      = __lc_input->mon_grouping;
+    __positive_sign_     = __lc_input->positive_sign;
+    __negative_sign_     = __lc_input->negative_sign;
+
+    __lc_.decimal_point     = const_cast<char*>(__decimal_point_.c_str());
+    __lc_.thousands_sep     = const_cast<char*>(__thousands_sep_.c_str());
+    __lc_.grouping          = const_cast<char*>(__grouping_.c_str());
+    __lc_.int_curr_symbol   = const_cast<char*>(__int_curr_symbol_.c_str());
+    __lc_.currency_symbol   = const_cast<char*>(__currency_symbol_.c_str());
+    __lc_.mon_decimal_point = const_cast<char*>(__mon_decimal_point_.c_str());
+    __lc_.mon_thousands_sep = const_cast<char*>(__mon_thousands_sep_.c_str());
+    __lc_.mon_grouping      = const_cast<char*>(__mon_grouping_.c_str());
+    __lc_.positive_sign     = const_cast<char*>(__positive_sign_.c_str());
+    __lc_.negative_sign     = const_cast<char*>(__negative_sign_.c_str());
+  }
+
+  std::lconv* __get() { return &__lc_; }
+
+private:
+  std::lconv __lc_;
+  std::string __decimal_point_;
+  std::string __thousands_sep_;
+  std::string __grouping_;
+  std::string __int_curr_symbol_;
+  std::string __currency_symbol_;
+  std::string __mon_decimal_point_;
+  std::string __mon_thousands_sep_;
+  std::string __mon_grouping_;
+  std::string __positive_sign_;
+  std::string __negative_sign_;
+};
+
+//
+// Locale management
+//
+class __locale_t {
+public:
+  __locale_t() : __locale_(nullptr), __locale_str_(nullptr), __lc_(nullptr) {}
+  __locale_t(std::nullptr_t) : __locale_(nullptr), __locale_str_(nullptr), __lc_(nullptr) {}
+  __locale_t(::_locale_t __loc, const char* __loc_str) : __locale_(__loc), __locale_str_(__loc_str), __lc_(nullptr) {}
+  __locale_t(const __locale_t& __loc)
+      : __locale_(__loc.__locale_), __locale_str_(__loc.__locale_str_), __lc_(nullptr) {}
+
+  ~__locale_t() { delete __lc_; }
+
+  __locale_t& operator=(const __locale_t& __loc) {
+    __locale_     = __loc.__locale_;
+    __locale_str_ = __loc.__locale_str_;
+    // __lc_ not copied
+    return *this;
+  }
+
+  friend bool operator==(const __locale_t& __left, const __locale_t& __right) {
+    return __left.__locale_ == __right.__locale_;
+  }
+
+  friend bool operator==(const __locale_t& __left, int __right) { return __left.__locale_ == nullptr && __right == 0; }
+
+  friend bool operator==(const __locale_t& __left, long long __right) {
+    return __left.__locale_ == nullptr && __right == 0;
+  }
+
+  friend bool operator==(const __locale_t& __left, std::nullptr_t) { return __left.__locale_ == nullptr; }
+
+  friend bool operator==(int __left, const __locale_t& __right) { return __left == 0 && nullptr == __right.__locale_; }
+
+  friend bool operator==(std::nullptr_t, const __locale_t& __right) { return nullptr == __right.__locale_; }
+
+  friend bool operator!=(const __locale_t& __left, const __locale_t& __right) { return !(__left == __right); }
+
+  friend bool operator!=(const __locale_t& __left, int __right) { return !(__left == __right); }
+
+  friend bool operator!=(const __locale_t& __left, long long __right) { return !(__left == __right); }
+
+  friend bool operator!=(const __locale_t& __left, std::nullptr_t __right) { return !(__left == __right); }
+
+  friend bool operator!=(int __left, const __locale_t& __right) { return !(__left == __right); }
+
+  friend bool operator!=(std::nullptr_t __left, const __locale_t& __right) { return !(__left == __right); }
+
+  operator bool() const { return __locale_ != nullptr; }
+
+  const char* __get_locale() const { return __locale_str_; }
+
+  operator ::_locale_t() const { return __locale_; }
+
+  std::lconv* __store_lconv(const std::lconv* __input_lc) {
+    delete __lc_;
+    __lc_ = new __lconv_storage(__input_lc);
+    return __lc_->__get();
+  }
+
+private:
+  ::_locale_t __locale_;
+  const char* __locale_str_;
+  __lconv_storage* __lc_ = nullptr;
+};
+
+// __uselocale can't be implemented on Windows because Windows allows partial modification
+// of thread-local locale and so _get_current_locale() returns a copy while __uselocale does
+// not create any copies. We can still implement RAII even without __uselocale though.
+__locale_t __uselocale(__locale_t) = delete;
+_LIBCPP_EXPORTED_FROM_ABI __locale_t __newlocale(int __mask, const char* __locale, __locale_t __base);
+inline _LIBCPP_HIDE_FROM_ABI void __freelocale(__locale_t __loc) { ::_free_locale(__loc); }
+_LIBCPP_EXPORTED_FROM_ABI lconv* __localeconv(__locale_t& __loc);
+
+//
+// Strtonum functions
+//
+
+// the *_l functions are prefixed on Windows, only available for msvcr80+, VS2005+
+#if defined(_LIBCPP_MSVCRT)
+inline _LIBCPP_HIDE_FROM_ABI float __strtof(const char* __nptr, char** __endptr, __locale_t __loc) {
+  return ::_strtof_l(__nptr, __endptr, __loc);
+}
+inline _LIBCPP_HIDE_FROM_ABI long double __strtold(const char* __nptr, char** __endptr, __locale_t __loc) {
+  return ::_strtold_l(__nptr, __endptr, __loc);
+}
+#else
+_LIBCPP_EXPORTED_FROM_ABI float __strtof(const char*, char**, __locale_t);
+_LIBCPP_EXPORTED_FROM_ABI long double __strtold(const char*, char**, __locale_t);
+#endif
+
+inline _LIBCPP_HIDE_FROM_ABI double __strtod(const char* __nptr, char** __endptr, __locale_t __loc) {
+  return ::_strtod_l(__nptr, __endptr, __loc);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI long long __strtoll(const char* __nptr, char** __endptr, int __base, __locale_t __loc) {
+  return ::_strtoi64_l(__nptr, __endptr, __base, __loc);
+}
+inline _LIBCPP_HIDE_FROM_ABI unsigned long long
+__strtoull(const char* __nptr, char** __endptr, int __base, __locale_t __loc) {
+  return ::_strtoui64_l(__nptr, __endptr, __base, __loc);
+}
+
+//
+// Character manipulation functions
+//
+inline _LIBCPP_HIDE_FROM_ABI int __islower(int __c, _locale_t __loc) { return ::_islower_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __isupper(int __c, _locale_t __loc) { return ::_isupper_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __isdigit(int __c, _locale_t __loc) { return ::_isdigit_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __isxdigit(int __c, _locale_t __loc) { return ::_isxdigit_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __toupper(int __c, __locale_t __loc) { return ::_toupper_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __tolower(int __c, __locale_t __loc) { return ::_tolower_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __strcoll(const char* __s1, const char* __s2, __locale_t __loc) {
+  return ::_strcoll_l(__s1, __s2, __loc);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI size_t __strxfrm(char* __dest, const char* __src, size_t __n, __locale_t __loc) {
+  return ::_strxfrm_l(__dest, __src, __n, __loc);
+}
+
+#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
+inline _LIBCPP_HIDE_FROM_ABI int __iswspace(wint_t __c, __locale_t __loc) { return ::_iswspace_l(__c, __loc); }
+inline _LIBCPP_HIDE_FROM_ABI int __iswprint(wint_t __c, __locale_t __loc) { return ::_iswprint_l(__c, __loc); }
+inline _LIBCPP_HIDE_FROM_ABI int __iswcntrl(wint_t __c, __locale_t __loc) { return ::_iswcntrl_l(__c, __loc); }
+inline _LIBCPP_HIDE_FROM_ABI int __iswupper(wint_t __c, __locale_t __loc) { return ::_iswupper_l(__c, __loc); }
+inline _LIBCPP_HIDE_FROM_ABI int __iswlower(wint_t __c, __locale_t __loc) { return ::_iswlower_l(__c, __loc); }
+inline _LIBCPP_HIDE_FROM_ABI int __iswalpha(wint_t __c, __locale_t __loc) { return ::_iswalpha_l(__c, __loc); }
+// TODO: use locale to determine blank characters
+inline _LIBCPP_HIDE_FROM_ABI int __iswblank(wint_t __c, __locale_t /*loc*/) { return (__c == L' ' || __c == L'\t'); }
+inline _LIBCPP_HIDE_FROM_ABI int __iswdigit(wint_t __c, __locale_t __loc) { return ::_iswdigit_l(__c, __loc); }
+inline _LIBCPP_HIDE_FROM_ABI int __iswpunct(wint_t __c, __locale_t __loc) { return ::_iswpunct_l(__c, __loc); }
+inline _LIBCPP_HIDE_FROM_ABI int __iswxdigit(wint_t __c, __locale_t __loc) { return ::_iswxdigit_l(__c, __loc); }
+inline _LIBCPP_HIDE_FROM_ABI wint_t __towupper(wint_t __c, __locale_t __loc) { return ::_towupper_l(__c, __loc); }
+inline _LIBCPP_HIDE_FROM_ABI wint_t __towlower(wint_t __c, __locale_t __loc) { return ::_towlower_l(__c, __loc); }
+
+inline _LIBCPP_HIDE_FROM_ABI int __wcscoll(const wchar_t* __ws1, const wchar_t* __ws2, __locale_t __loc) {
+  return ::_wcscoll_l(__ws1, __ws2, __loc);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI size_t __wcsxfrm(wchar_t* __dest, const wchar_t* __src, size_t __n, __locale_t __loc) {
+  return ::_wcsxfrm_l(__dest, __src, __n, __loc);
+}
+#endif // !_LIBCPP_HAS_NO_WIDE_CHARACTERS
+
+#if defined(__MINGW32__) && __MSVCRT_VERSION__ < 0x0800
+_LIBCPP_EXPORTED_FROM_ABI size_t __strftime(char*, size_t, const char*, const struct tm*, __locale_t);
+#else
+inline _LIBCPP_HIDE_FROM_ABI size_t
+__strftime(char* __ret, size_t __n, const char* __format, const struct tm* __tm, __locale_t __loc) {
+  return ::_strftime_l(__ret, __n, __format, __tm, __loc);
+}
+#endif
+
+//
+// Other functions
+//
+_LIBCPP_EXPORTED_FROM_ABI decltype(MB_CUR_MAX) __mb_len_max(__locale_t);
+_LIBCPP_EXPORTED_FROM_ABI wint_t __btowc(int, __locale_t);
+_LIBCPP_EXPORTED_FROM_ABI int __wctob(wint_t, __locale_t);
+_LIBCPP_EXPORTED_FROM_ABI size_t
+__wcsnrtombs(char* __restrict, const wchar_t** __restrict, size_t, size_t, mbstate_t* __restrict, __locale_t);
+_LIBCPP_EXPORTED_FROM_ABI size_t __wcrtomb(char* __restrict, wchar_t, mbstate_t* __restrict, __locale_t);
+_LIBCPP_EXPORTED_FROM_ABI size_t
+__mbsnrtowcs(wchar_t* __restrict, const char** __restrict, size_t, size_t, mbstate_t* __restrict, __locale_t);
+_LIBCPP_EXPORTED_FROM_ABI size_t
+__mbrtowc(wchar_t* __restrict, const char* __restrict, size_t, mbstate_t* __restrict, __locale_t);
+
+inline _LIBCPP_HIDE_FROM_ABI int __mbtowc(wchar_t* __pwc, const char* __pmb, size_t __max, __locale_t __loc) {
+  return ::_mbtowc_l(__pwc, __pmb, __max, __loc);
+}
+
+_LIBCPP_EXPORTED_FROM_ABI size_t __mbrlen(const char* __restrict, size_t, mbstate_t* __restrict, __locale_t);
+
+_LIBCPP_EXPORTED_FROM_ABI size_t
+__mbsrtowcs(wchar_t* __restrict, const char** __restrict, size_t, mbstate_t* __restrict, __locale_t);
+
+_LIBCPP_EXPORTED_FROM_ABI _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 4, 5) int __snprintf(
+    char* __ret, size_t __n, __locale_t __loc, const char* __format, ...);
+
+_LIBCPP_EXPORTED_FROM_ABI
+_LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4) int __asprintf(char** __ret, __locale_t __loc, const char* __format, ...);
+
+_LIBCPP_DIAGNOSTIC_PUSH
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wgcc-compat")
+_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wformat-nonliteral") // GCC doesn't support [[gnu::format]] on variadic templates
+#ifdef _LIBCPP_COMPILER_CLANG_BASED
+#  define _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(...) _LIBCPP_ATTRIBUTE_FORMAT(__VA_ARGS__)
+#else
+#  define _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(...) /* nothing */
+#endif
+
+template <class... _Args>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT(__scanf__, 3, 4) int __sscanf(
+    const char* __dest, __locale_t __loc, const char* __format, _Args&&... __args) {
+  return ::_sscanf_l(__dest, __format, __loc, std::forward<_Args>(__args)...);
+}
+_LIBCPP_DIAGNOSTIC_POP
+#undef _LIBCPP_VARIADIC_ATTRIBUTE_FORMAT
+
+} // namespace __locale
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___LOCALE_DIR_SUPPORT_WINDOWS_H
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index ed2b7fb1921642..e553587826c9de 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1473,6 +1473,7 @@ module std [system] {
       textual header "__locale_dir/support/apple.h"
       textual header "__locale_dir/support/bsd_like.h"
       textual header "__locale_dir/support/freebsd.h"
+      textual header "__locale_dir/support/windows.h"
     }
 
     module locale_base_api {
@@ -1483,7 +1484,6 @@ module std [system] {
       textual header "__locale_dir/locale_base_api/ibm.h"
       textual header "__locale_dir/locale_base_api/musl.h"
       textual header "__locale_dir/locale_base_api/openbsd.h"
-      textual header "__locale_dir/locale_base_api/win32.h"
     }
     export *
   }
diff --git a/libcxx/src/support/win32/locale_win32.cpp b/libcxx/src/support/win32/locale_win32.cpp
index 2a08e97b8645b4..c59de492f9c8e2 100644
--- a/libcxx/src/support/win32/locale_win32.cpp
+++ b/libcxx/src/support/win32/locale_win32.cpp
@@ -6,125 +6,172 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include <cstdarg> // va_start, va_end
-#include <locale>
-#include <memory>
-#include <type_traits>
-
 #include <__locale_dir/locale_guard.h>
+#include <__locale_dir/support/windows.h>
+#include <clocale> // std::localeconv() & friends
+#include <cstdarg> // va_start & friends
+#include <cstddef>
+#include <cstdio>  // std::vsnprintf & friends
+#include <cstdlib> // std::strtof & friends
+#include <ctime>   // std::strftime
+#include <cwchar>  // wide char manipulation
 
-int __libcpp_vasprintf(char** sptr, const char* __restrict fmt, va_list ap);
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __locale {
 
+//
+// Locale management
+//
 // FIXME: base and mask currently unused. Needs manual work to construct the new locale
-locale_t newlocale(int /*mask*/, const char* locale, locale_t /*base*/) {
-  return {_create_locale(LC_ALL, locale), locale};
+__locale_t __newlocale(int /*mask*/, const char* locale, __locale_t /*base*/) {
+  return {::_create_locale(LC_ALL, locale), locale};
+}
+
+lconv* __localeconv(__locale_t& loc) {
+  std::__locale_guard __current(loc);
+  lconv* lc = std::localeconv();
+  if (!lc)
+    return lc;
+  return loc.__store_lconv(lc);
+}
+
+//
+// Strtonum functions
+//
+#if !defined(_LIBCPP_MSVCRT)
+float __strtof(const char* nptr, char** endptr, __locale_t loc) {
+  std::__locale_guard __current(loc);
+  return std::strtof(nptr, endptr);
+}
+
+long double __strtold(const char* nptr, char** endptr, __locale_t loc) {
+  std::__locale_guard __current(loc);
+  return std::strtold(nptr, endptr);
 }
+#endif
 
-decltype(MB_CUR_MAX) MB_CUR_MAX_L(locale_t __l) {
+//
+// Character manipulation functions
+//
+#if defined(__MINGW32__) && __MSVCRT_VERSION__ < 0x0800
+size_t __strftime(char* ret, size_t n, const char* format, const struct tm* tm, __locale_t loc) {
+  std::__locale_guard __current(loc);
+  return std::strftime(ret, n, format, tm);
+}
+#endif
+
+//
+// Other functions
+//
+decltype(MB_CUR_MAX) __mb_len_max(__locale_t __l) {
 #if defined(_LIBCPP_MSVCRT)
-  return ___mb_cur_max_l_func(__l);
+  return ::___mb_cur_max_l_func(__l);
 #else
   std::__locale_guard __current(__l);
   return MB_CUR_MAX;
 #endif
 }
 
-lconv* localeconv_l(locale_t& loc) {
+wint_t __btowc(int c, __locale_t loc) {
   std::__locale_guard __current(loc);
-  lconv* lc = localeconv();
-  if (!lc)
-    return lc;
-  return loc.__store_lconv(lc);
+  return std::btowc(c);
 }
-size_t mbrlen_l(const char* __restrict s, size_t n, mbstate_t* __restrict ps, locale_t loc) {
-  std::__locale_guard __current(loc);
-  return mbrlen(s, n, ps);
-}
-size_t
-mbsrtowcs_l(wchar_t* __restrict dst, const char** __restrict src, size_t len, mbstate_t* __restrict ps, locale_t loc) {
+
+int __wctob(wint_t c, __locale_t loc) {
   std::__locale_guard __current(loc);
-  return mbsrtowcs(dst, src, len, ps);
+  return std::wctob(c);
 }
-size_t wcrtomb_l(char* __restrict s, wchar_t wc, mbstate_t* __restrict ps, locale_t loc) {
+
+size_t __wcsnrtombs(char* __restrict dst,
+                    const wchar_t** __restrict src,
+                    size_t nwc,
+                    size_t len,
+                    mbstate_t* __restrict ps,
+                    __locale_t loc) {
   std::__locale_guard __current(loc);
-  return wcrtomb(s, wc, ps);
+  return ::wcsnrtombs(dst, src, nwc, len, ps);
 }
-size_t mbrtowc_l(wchar_t* __restrict pwc, const char* __restrict s, size_t n, mbstate_t* __restrict ps, locale_t loc) {
+
+size_t __wcrtomb(char* __restrict s, wchar_t wc, mbstate_t* __restrict ps, __locale_t loc) {
   std::__locale_guard __current(loc);
-  return mbrtowc(pwc, s, n, ps);
+  return std::wcrtomb(s, wc, ps);
 }
-size_t mbsnrtowcs_l(wchar_t* __restrict dst,
+
+size_t __mbsnrtowcs(wchar_t* __restrict dst,
                     const char** __restrict src,
                     size_t nms,
                     size_t len,
                     mbstate_t* __restrict ps,
-                    locale_t loc) {
+                    __locale_t loc) {
   std::__locale_guard __current(loc);
-  return mbsnrtowcs(dst, src, nms, len, ps);
+  return ::mbsnrtowcs(dst, src, nms, len, ps);
 }
-size_t wcsnrtombs_l(char* __restrict dst,
-                    const wchar_t** __restrict src,
-                    size_t nwc,
-                    size_t len,
-                    mbstate_t* __restrict ps,
-                    locale_t loc) {
+
+size_t
+__mbrtowc(wchar_t* __restrict pwc, const char* __restrict s, size_t n, mbstate_t* __restrict ps, __locale_t loc) {
   std::__locale_guard __current(loc);
-  return wcsnrtombs(dst, src, nwc, len, ps);
+  return std::mbrtowc(pwc, s, n, ps);
 }
-wint_t btowc_l(int c, locale_t loc) {
+
+size_t __mbrlen(const char* __restrict s, size_t n, mbstate_t* __restrict ps, __locale_t loc) {
   std::__locale_guard __current(loc);
-  return btowc(c);
+  return std::mbrlen(s, n, ps);
 }
-int wctob_l(wint_t c, locale_t loc) {
+
+size_t __mbsrtowcs(
+    wchar_t* __restrict dst, const char** __restrict src, size_t len, mbstate_t* __restrict ps, __locale_t loc) {
   std::__locale_guard __current(loc);
-  return wctob(c);
+  return std::mbsrtowcs(dst, src, len, ps);
 }
 
-int snprintf_l(char* ret, size_t n, locale_t loc, const char* format, ...) {
+int __snprintf(char* ret, size_t n, __locale_t loc, const char* format, ...) {
   va_list ap;
   va_start(ap, format);
 #if defined(_LIBCPP_MSVCRT)
   // FIXME: Remove usage of internal CRT function and globals.
-  int result = __stdio_common_vsprintf(
+  int result = ::__stdio_common_vsprintf(
       _CRT_INTERNAL_LOCAL_PRINTF_OPTIONS | _CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR, ret, n, format, loc, ap);
 #else
   std::__locale_guard __current(loc);
   _LIBCPP_DIAGNOSTIC_PUSH
   _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wformat-nonliteral")
-  int result = vsnprintf(ret, n, format, ap);
+  int result = std::vsnprintf(ret, n, format, ap);
   _LIBCPP_DIAGNOSTIC_POP
 #endif
   va_end(ap);
   return result;
 }
 
-int asprintf_l(char** ret, locale_t loc, const char* format, ...) {
+int __asprintf(char** ret, __locale_t loc, const char* format, ...) {
   va_list ap;
   va_start(ap, format);
-  int result = vasprintf_l(ret, loc, format, ap);
-  va_end(ap);
-  return result;
-}
-int vasprintf_l(char** ret, locale_t loc, const char* format, va_list ap) {
   std::__locale_guard __current(loc);
-  return __libcpp_vasprintf(ret, format, ap);
-}
-
-#if !defined(_LIBCPP_MSVCRT)
-float strtof_l(const char* nptr, char** endptr, locale_t loc) {
-  std::__locale_guard __current(loc);
-  return strtof(nptr, endptr);
-}
-
-long double strtold_l(const char* nptr, char** endptr, locale_t loc) {
-  std::__locale_guard __current(loc);
-  return strtold(nptr, endptr);
+  *sptr = nullptr;
+  // Query the count required.
+  _LIBCPP_DIAGNOSTIC_PUSH
+  _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wformat-nonliteral")
+  int count = std::vsnprintf(nullptr, 0, format, ap);
+  _LIBCPP_DIAGNOSTIC_POP
+  va_end(ap);
+  if (count < 0)
+    return count;
+  size_t buffer_size = static_cast<size_t>(count) + 1;
+  char* p            = static_cast<char*>(std::malloc(buffer_size));
+  if (!p)
+    return -1;
+  // If we haven't used exactly what was required, something is wrong.
+  // Maybe bug in vsnprintf. Report the error and return.
+  _LIBCPP_DIAGNOSTIC_PUSH
+  _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wformat-nonliteral")
+  if (std::vsnprintf(p, buffer_size, format, ap) != count) {
+    _LIBCPP_DIAGNOSTIC_POP
+    std::free(p);
+    return -1;
+  }
+  // All good. This is returning memory to the caller not freeing it.
+  *sptr = p;
+  return count;
 }
-#endif
 
-#if defined(__MINGW32__) && __MSVCRT_VERSION__ < 0x0800
-size_t strftime_l(char* ret, size_t n, const char* format, const struct tm* tm, locale_t loc) {
-  std::__locale_guard __current(loc);
-  return strftime(ret, n, format, tm);
-}
-#endif
+} // namespace __locale
+_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/support/win32/support.cpp b/libcxx/src/support/win32/support.cpp
index ccf5ff5acb4581..7ac508301b526c 100644
--- a/libcxx/src/support/win32/support.cpp
+++ b/libcxx/src/support/win32/support.cpp
@@ -13,39 +13,6 @@
 #include <cstring> // strcpy, wcsncpy
 #include <cwchar>  // mbstate_t
 
-// Like sprintf, but when return value >= 0 it returns
-// a pointer to a malloc'd string in *sptr.
-// If return >= 0, use free to delete *sptr.
-int __libcpp_vasprintf(char** sptr, const char* __restrict format, va_list ap) {
-  *sptr = nullptr;
-  // Query the count required.
-  va_list ap_copy;
-  va_copy(ap_copy, ap);
-  _LIBCPP_DIAGNOSTIC_PUSH
-  _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wformat-nonliteral")
-  int count = vsnprintf(nullptr, 0, format, ap_copy);
-  _LIBCPP_DIAGNOSTIC_POP
-  va_end(ap_copy);
-  if (count < 0)
-    return count;
-  size_t buffer_size = static_cast<size_t>(count) + 1;
-  char* p            = static_cast<char*>(malloc(buffer_size));
-  if (!p)
-    return -1;
-  // If we haven't used exactly what was required, something is wrong.
-  // Maybe bug in vsnprintf. Report the error and return.
-  _LIBCPP_DIAGNOSTIC_PUSH
-  _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wformat-nonliteral")
-  if (vsnprintf(p, buffer_size, format, ap) != count) {
-    _LIBCPP_DIAGNOSTIC_POP
-    free(p);
-    return -1;
-  }
-  // All good. This is returning memory to the caller not freeing it.
-  *sptr = p;
-  return count;
-}
-
 // Returns >= 0: the number of wide characters found in the
 // multi byte sequence src (of src_size_bytes), that fit in the buffer dst
 // (of max_dest_chars elements size). The count returned excludes the



More information about the libcxx-commits mailing list