From 3687ea0f055039a8d2f2eecd4067b2f8db1fac99 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Tue, 27 Jan 2026 18:12:12 +0800 Subject: [PATCH 1/4] Implement LWG-4037 --- stl/inc/xlocale | 38 ++++++++++--------- .../tests/Dev11_1074023_constexpr/test.cpp | 34 +++++++++++++++++ 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index 260c1c55b55..e89a518c87f 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -2369,25 +2369,29 @@ _STL_RESTORE_DEPRECATED_WARNING #define _UP _UPPER // 'A'-'Z' #define _XD _HEX // '0'-'9', 'A'-'F', 'a'-'f' -_EXPORT_STD extern "C++" struct _CRTIMP2_PURE_IMPORT ctype_base // base for ctype - : locale::facet // TRANSITION, ABI, shouldn't be derived from locale::facet -{ - enum { // constants for character classifications - alnum = _DI | _LO | _UP | _XA, - alpha = _LO | _UP | _XA, - cntrl = _BB, - digit = _DI, - graph = _DI | _LO | _PU | _UP | _XA, - lower = _LO, - print = _DI | _LO | _PU | _SP | _UP | _XA | _XD, - punct = _PU, - space = _CN | _SP, - upper = _UP, - xdigit = _XD, - blank = _CN | _SP - }; +struct _Ctype_constants_base { using mask = short; // to match + // constants for character classifications + + static constexpr mask alnum = _DI | _LO | _UP | _XA; + static constexpr mask alpha = _LO | _UP | _XA; + static constexpr mask cntrl = _BB; + static constexpr mask digit = _DI; + static constexpr mask graph = _DI | _LO | _PU | _UP | _XA; + static constexpr mask lower = _LO; + static constexpr mask print = _DI | _LO | _PU | _SP | _UP | _XA | _XD; + static constexpr mask punct = _PU; + static constexpr mask space = _CN | _SP; + static constexpr mask upper = _UP; + static constexpr mask xdigit = _XD; + static constexpr mask blank = _CN | _SP; +}; + +_EXPORT_STD extern "C++" struct _CRTIMP2_PURE_IMPORT ctype_base // base for ctype + : locale::facet, // TRANSITION, ABI, shouldn't be derived from locale::facet + _Ctype_constants_base // TRANSITION, ABI, avoid affecting DLL interface +{ __CLR_OR_THIS_CALL ctype_base(size_t _Refs = 0) noexcept // strengthened : locale::facet(_Refs) {} diff --git a/tests/std/tests/Dev11_1074023_constexpr/test.cpp b/tests/std/tests/Dev11_1074023_constexpr/test.cpp index 47092a0a47e..a7f6b6c832b 100644 --- a/tests/std/tests/Dev11_1074023_constexpr/test.cpp +++ b/tests/std/tests/Dev11_1074023_constexpr/test.cpp @@ -801,6 +801,12 @@ void test_all_constants() { test_constants(); + // LWG-4037 "Static data members of ctype_base are not yet required to be usable in constant expressions" + // The following furtherly requires ctype_base::mask to be usable as a type of a constant template parameter. + test_constants(); + test_constants(); @@ -814,6 +820,34 @@ void test_all_constants() { RC::error_badrepeat, RC::error_complexity, RC::error_stack>(); } +// Ensure that these members of ctype_base are actually implemented as static const members of the correct type. + +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); + +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); +STATIC_ASSERT(is_same_v); + constexpr csub_match sm{}; STATIC_ASSERT(sm.first == nullptr); STATIC_ASSERT(sm.second == nullptr); From 0fb1785d9197a3b68b384b2dd55f8ed6cdab4f29 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 11 Feb 2026 07:14:51 -0800 Subject: [PATCH 2/4] Add `__declspec(empty_bases)` to `ctype_base`. --- stl/inc/xlocale | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index 9f9433cb671..1fcf12d626c 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -2392,7 +2392,7 @@ struct _Ctype_constants_base { static constexpr mask blank = _CN | _SP; }; -_EXPORT_STD extern "C++" struct _CRTIMP2_PURE_IMPORT ctype_base // base for ctype +_EXPORT_STD extern "C++" struct _CRTIMP2_PURE_IMPORT __declspec(empty_bases) ctype_base // base for ctype : locale::facet, // TRANSITION, ABI, shouldn't be derived from locale::facet _Ctype_constants_base // TRANSITION, ABI, avoid affecting DLL interface { From da0918ae24e1e481577cb5b905a79a40aac09784 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 11 Feb 2026 07:57:35 -0800 Subject: [PATCH 3/4] ``: Remove enums. --- stl/inc/xiosbase | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/stl/inc/xiosbase b/stl/inc/xiosbase index 09cd9d407aa..0d90857d850 100644 --- a/stl/inc/xiosbase +++ b/stl/inc/xiosbase @@ -20,12 +20,11 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN template -class _Iosb { // define templatized bitmask/enumerated types, instantiate on demand +class _Iosb { // define templatized bitmask types, instantiate on demand public: - enum _Fmtflags { // constants for formatting options - _Fmtmask = 0xffff, - _Fmtzero = 0 - }; + // constants for formatting options + static constexpr int _Fmtmask = 0xffff; + static constexpr int _Fmtzero = 0; static constexpr int skipws = 0x0001; static constexpr int unitbuf = 0x0002; @@ -47,9 +46,8 @@ public: static constexpr int basefield = dec | oct | hex; static constexpr int floatfield = scientific | fixed; - enum _Iostate { // constants for stream states - _Statmask = 0x17 - }; + // constants for stream states + static constexpr int _Statmask = 0x17; static constexpr int goodbit = 0x0; static constexpr int eofbit = 0x1; @@ -73,13 +71,6 @@ public: static constexpr int end = 2; static constexpr int _Default_open_prot = _SH_DENYNO; // constant for default file opening protection - - // TRANSITION, ABI: These enums don't appear in the STL DLL's export surface, but MaxMPDCompat - // in the MSVC-internal build detects that they appear in the CompatibilityData baseline. - enum _Dummy_enum { _Dummy_enum_val = 1 }; - enum _Openmode { _Openmask = 0xff }; - enum _Seekdir { _Seekbeg, _Seekcur, _Seekend }; - enum { _Openprot = _SH_DENYNO }; }; _EXPORT_STD extern "C++" class _CRTIMP2_PURE_IMPORT ios_base : public _Iosb { // base class for ios From b4e3ecbb3365e336e4abb8964b3ae9ce223ea8cc Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 11 Feb 2026 12:51:35 -0800 Subject: [PATCH 4/4] Hiss. --- stl/inc/xlocale | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index 1fcf12d626c..f9f33468c60 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -21,6 +21,10 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new +// TRANSITION, non-_Ugly attribute tokens +#pragma push_macro("empty_bases") +#undef empty_bases + _STD_BEGIN template class _Locbase {}; // TRANSITION, ABI, affects sizeof(locale) @@ -3316,6 +3320,10 @@ template class _CRTIMP2_PURE_IMPORT codecvt; #endif // !defined(_CRTBLD) || defined(__FORCE_INSTANCE) #endif // defined(_DLL_CPPLIB) _STD_END + +// TRANSITION, non-_Ugly attribute tokens +#pragma pop_macro("empty_bases") + #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop)