Allow to set locale on Windows.

Fix the problem PR31516 with setting locale on Windows by wrapping
_locale_t with a pointer-like class.

Reduces 74 test failures in std/localization test suite to 47 test
failures (on llvm clang, Visual Studio 2015). Number of test failures
doesn't depend on the platform (x86 or x64).

Patch by Andrey Khalyavin.

Differential Revision: https://reviews.llvm.org/D40181

git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@318902 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/__config b/include/__config
index 70a34e1..abb487d 100644
--- a/include/__config
+++ b/include/__config
@@ -890,7 +890,7 @@
 #define _LIBCPP_NONUNIQUE_RTTI_BIT (1ULL << 63)
 #endif
 
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(_LIBCPP_MSVCRT) ||   \
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(_LIBCPP_MSVCRT_LIKE) || \
     defined(__sun__) || defined(__NetBSD__) || defined(__CloudABI__)
 #define _LIBCPP_LOCALE__L_EXTENSIONS 1
 #endif
diff --git a/include/__locale b/include/__locale
index 91ed9e7..601f0d1 100644
--- a/include/__locale
+++ b/include/__locale
@@ -49,7 +49,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if !defined(_LIBCPP_LOCALE__L_EXTENSIONS) || defined(_LIBCPP_MSVCRT)
+#if !defined(_LIBCPP_LOCALE__L_EXTENSIONS)
 struct __libcpp_locale_guard {
   _LIBCPP_INLINE_VISIBILITY
   __libcpp_locale_guard(locale_t& __loc) : __old_loc_(uselocale(__loc)) {}
@@ -65,6 +65,32 @@
   __libcpp_locale_guard(__libcpp_locale_guard const&);
   __libcpp_locale_guard& operator=(__libcpp_locale_guard const&);
 };
+#elif defined(_LIBCPP_MSVCRT_LIKE)
+struct __libcpp_locale_guard {
+    __libcpp_locale_guard(locale_t __l) :
+        __status(_configthreadlocale(_ENABLE_PER_THREAD_LOCALE)),
+        __locale_collate(setlocale(LC_COLLATE, __l.__get_locale())),
+        __locale_ctype(setlocale(LC_CTYPE, __l.__get_locale())),
+        __locale_monetary(setlocale(LC_MONETARY, __l.__get_locale())),
+        __locale_numeric(setlocale(LC_NUMERIC, __l.__get_locale())),
+        __locale_time(setlocale(LC_TIME, __l.__get_locale()))
+        // LC_MESSAGES is not supported on Windows.
+    {}
+    ~__libcpp_locale_guard() {
+        setlocale(LC_COLLATE, __locale_collate);
+        setlocale(LC_CTYPE, __locale_ctype);
+        setlocale(LC_MONETARY, __locale_monetary);
+        setlocale(LC_NUMERIC, __locale_numeric);
+        setlocale(LC_TIME, __locale_time);
+        _configthreadlocale(__status);
+    }
+    int __status;
+    char* __locale_collate;
+    char* __locale_ctype;
+    char* __locale_monetary;
+    char* __locale_numeric;
+    char* __locale_time;
+};
 #endif
 
 
diff --git a/include/support/win32/locale_win32.h b/include/support/win32/locale_win32.h
index ea7cc9c..aebfff2 100644
--- a/include/support/win32/locale_win32.h
+++ b/include/support/win32/locale_win32.h
@@ -14,6 +14,7 @@
 #include <__config>
 #include <stdio.h>
 #include <xlocinfo.h> // _locale_t
+#include <__nullptr>
 
 #define LC_COLLATE_MASK _M_COLLATE
 #define LC_CTYPE_MASK _M_CTYPE
@@ -28,13 +29,77 @@
                      | LC_NUMERIC_MASK \
                      | LC_TIME_MASK )
 
-#define locale_t _locale_t
+class locale_t {
+public:
+    locale_t()
+        : __locale(nullptr), __locale_str(nullptr) {}
+    locale_t(std::nullptr_t)
+        : __locale(nullptr), __locale_str(nullptr) {}
+    locale_t(_locale_t __locale, const char* __locale_str)
+        : __locale(__locale), __locale_str(__locale_str) {}
+
+    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, 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, 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;
+    }
+private:
+    _locale_t __locale;
+    const char* __locale_str;
+};
 
 // 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 );
-locale_t uselocale( locale_t newloc );
+// 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 );
diff --git a/src/support/win32/locale_win32.cpp b/src/support/win32/locale_win32.cpp
index 13a6eae..fdca7ef 100644
--- a/src/support/win32/locale_win32.cpp
+++ b/src/support/win32/locale_win32.cpp
@@ -18,21 +18,7 @@
 // FIXME: base currently unused. Needs manual work to construct the new locale
 locale_t newlocale( int mask, const char * locale, locale_t /*base*/ )
 {
-    return _create_locale( mask, locale );
-}
-
-locale_t uselocale( locale_t newloc )
-{
-    locale_t old_locale = _get_current_locale();
-    if ( newloc == NULL )
-        return old_locale;
-    // uselocale sets the thread's locale by definition, so unconditionally use thread-local locale
-    _configthreadlocale( _ENABLE_PER_THREAD_LOCALE );
-    // uselocale sets all categories
-    // disable setting locale on Windows temporarily because the structure is opaque (PR31516)
-    //setlocale( LC_ALL, newloc->locinfo->lc_category[LC_ALL].locale );
-    // uselocale returns the old locale_t
-    return old_locale;
+    return {_create_locale( LC_ALL, locale ), locale};
 }
 
 decltype(MB_CUR_MAX) MB_CUR_MAX_L( locale_t __l )