mettle
mettle copied to clipboard
Static initialization order fiasco with inline all_suites on Windows + Clang
I'm continuing my experiments with mettle on different platforms by incorporating into CI.
Recently found a weird issue - on Windows with Clang, all tests are silently skipped and the output is always:
0/0 tests passed
This is caused by a static initialization order issue, due to how the variable inline suites_list all_suites is defined in all_suites.hpp:
- Test suites in mettle are usually registered as static variables;
- The global
all_suitesvector is defined asinline; - On some platforms (notably Clang on Windows), these static test suite variables are initialized before the constructor of
all_suitesruns. - Once the
all_suitesconstructor runs, it clears its internal list.
The result: test suites register themselves before all_suites is constructed → later all_suites constructor wipes everything.
According to the C++ standard (basic.start.static §6.9.3.2/2):
Variables with static storage duration defined in the same translation unit shall be initialized in the order in which their definitions appear in the translation unit.
However, inline variables with external linkage may be emitted into a separate object file section, and on some toolchains like Windows+Clang, they are initialized after statics, violating the expected intra-TU order.
From my read of the standard, this seems like a compiler bug. See §6.9.3.3 p6:
It is implementation-defined whether the dynamic initialization of a non-block inline variable with static storage duration is sequenced before the first statement of main or is deferred. If it is deferred, it strongly happens before any non-initialization odr-use of that variable. It is implementation-defined in which threads and at which points in the program such deferred dynamic initialization occurs.
Since the suite constructor odr-uses all_suites, I think the standard says that the latter must be dynamically-initialized before the former.