From 540f6d11ccfc91272699b7caca16c1a016950bc4 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 21 Aug 2026 00:48:42 +0200 Subject: [PATCH] Do not use thread_local storage with Clang targeting MinGW Every test that copies a value segfaults there - 42 of 105 on clang 11.0.1, 39 of 102 on clang 18.1.8 - while the same tests pass with GCC targeting MinGW, with Clang targeting MSVC, and with every other toolchain the library is tested on. The counter that bounds the copy constructor's descent is the library's first use of thread_local, so that job had never exercised it before. JSON_NO_THREAD_LOCAL already covers toolchains without thread_local storage, and copying yields the same values with it, only more slowly. Define it for this one automatically. Signed-off-by: Niels Lohmann --- docs/mkdocs/docs/api/macros/json_no_thread_local.md | 3 +++ include/nlohmann/detail/macro_scope.hpp | 9 +++++++++ single_include/nlohmann/json.hpp | 9 +++++++++ 3 files changed, 21 insertions(+) diff --git a/docs/mkdocs/docs/api/macros/json_no_thread_local.md b/docs/mkdocs/docs/api/macros/json_no_thread_local.md index d98745243..126116ec3 100644 --- a/docs/mkdocs/docs/api/macros/json_no_thread_local.md +++ b/docs/mkdocs/docs/api/macros/json_no_thread_local.md @@ -26,6 +26,9 @@ By default, `#!cpp JSON_NO_THREAD_LOCAL` is not defined. #undef JSON_NO_THREAD_LOCAL ``` +The library defines it by itself for Clang targeting MinGW, which does not survive the `#!cpp thread_local` storage: +copying a value segfaults there, with both old and current Clang versions, while GCC targeting MinGW is unaffected. + ## Examples ??? example diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 4682fd361..e096fcb62 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -186,6 +186,15 @@ #define JSON_NO_UNIQUE_ADDRESS #endif +// Clang targeting MinGW does not survive the thread_local storage the copy +// constructor uses to bound its descent: every test that copies a value +// segfaults with clang 11.0.1 and clang 18.1.8, while the same tests pass with +// GCC targeting MinGW and with every other toolchain the library is tested on. +// Copying works the same way without the counter, only more slowly. +#if !defined(JSON_NO_THREAD_LOCAL) && defined(__clang__) && defined(__MINGW32__) + #define JSON_NO_THREAD_LOCAL 1 +#endif + // disable documentation warnings on clang #if defined(__clang__) #pragma clang diagnostic push diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 4ab4e4a92..192b0ad06 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2561,6 +2561,15 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define JSON_NO_UNIQUE_ADDRESS #endif +// Clang targeting MinGW does not survive the thread_local storage the copy +// constructor uses to bound its descent: every test that copies a value +// segfaults with clang 11.0.1 and clang 18.1.8, while the same tests pass with +// GCC targeting MinGW and with every other toolchain the library is tested on. +// Copying works the same way without the counter, only more slowly. +#if !defined(JSON_NO_THREAD_LOCAL) && defined(__clang__) && defined(__MINGW32__) + #define JSON_NO_THREAD_LOCAL 1 +#endif + // disable documentation warnings on clang #if defined(__clang__) #pragma clang diagnostic push