STL
STL copied to clipboard
<locale>: locale initialization incorrect in debug mode
Describe the bug locale::id classes are used to lazily assign numbers to facets classes. They have non-trivial constructors and when compiled in debug mode they are not initialized at compile time. This means that the following could happen:
- Some static constructor (std::cin for example) uses locales and causes id assignment for some facets.
- locale::id constructors for this facets are called which reset facet ids to zero.
- The program tries to use locales and different ids are assigned to facets which leads to errors or crashes.
Command-line test case
C:\Temp>type main.cpp
#include <locale>
#include <iostream>
struct S {
size_t id;
S() {
id = std::collate<char>::id;
}
};
S v;
int main() {
std::cout << v.id << std::endl;
std::cout << std::collate<char>::id << std::endl;
}
C:\Temp>cl /EHsc /W4 /WX /MT main.cpp
Оптимизирующий компилятор Microsoft (R) C/C++ версии 19.28.29115 для x64
(C) Корпорация Майкрософт (Microsoft Corporation). Все права защищены.
main.cpp
Microsoft (R) Incremental Linker Version 14.28.29115.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:main.exe
main.obj
C:\Temp>main.exe
2
5
Expected behavior Either compiler must initialize locale::id's at compile time in debug mode or locale::id class should be rewritten to avoid this problem.
STL version
Microsoft Visual Studio Community 2019 Preview Version 16.8.0 Preview 1.0
Additional context DevCom-256346 | Microsoft-internal VSO-620209 / AB#620209 and VSO-622201 / AB#622201
Describe the bug locale::id classes are used to lazily assign numbers to facets classes. They have non-trivial constructors and when compiled in debug mode they are not initialized at compile time. There is however no requirement made to facets that the id must be fully constructed prior to their usage, or that locales cannot be used during static construction, or that use_facet / has_facet cannot be called prior to the construction of the id member. The only requirement for a facet is for such a member to exist and to derive from std::locale::facet and have a static member of this form, see ISO C++17, 25.3.1.1.2-1 and the comment 231.
Command-line test case
C:\Temp>type main.cpp
#include <locale>
#include <iostream>
struct myfacet : public std::locale::facet {
static std::locale::id id;
};
std::locale loc1(std::locale::classic(), new myfacet);
std::locale::id myfacet::id;
std::locale loc2(std::locale::classic(), new myfacet);
int main() {
std::cout << "loc1 has myfacet? "
<< std::boolalpha
<< std::has_facet<myfacet>(loc1) << std::endl;
std::cout << "loc2 has myfacet? "
<< std::boolalpha
<< std::has_facet<myfacet>(loc2) << std::endl;
}
C:\Temp>cl /EHsc /W4 /WX main.cpp
Оптимизирующий компилятор Microsoft (R) C/C++ версии 19.28.29115 для x64
(C) Корпорация Майкрософт (Microsoft Corporation). Все права защищены.
main.cpp
Microsoft (R) Incremental Linker Version 14.28.29115.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:main.exe
main.obj
C:\Temp>main.exe
loc1 has myfacet? false
loc2 has myfacet? true
Expected behavior Either compiler must initialize locale::id's at compile time in debug mode or locale::id class should be rewritten to avoid this problem. And repro should print:
loc1 has myfacet? true
loc2 has myfacet? true
STL version
Microsoft Visual Studio Community 2019 Preview Version 16.8.0 Preview 1.0
Additional context VSO-622201 | DevCom-260195
Both testcases work fine in Release Mode
Comments from the MS-internal bugs (which may or may not be correct, especially mine :joy_cat:):
@BillyONeal
As far as I can tell, this is completely conforming behavior. There is no requirement that
locale::idbe statically initializable.
@StephanTLavavej
We need the static init compiler bug to be fixed [2020-08-10 note: it has been fixed], we need to mark the
locale::idconstructor asconstexpr, and then we investigate whether WCFB02 can support this.
@amyw-msft
This also repros in release mode (/MT). When compiled with /MD, the dynamic initialization of
collate<char>::idprior toSis guaranteed since the DLL will load first. In /MT it is not guaranteed. We should investigate adding some ordering with#pragma init_segininstances.cppwhere these locale ids are defined.