Fix false GCC -Warray-bounds error with JSON_DIAGNOSTICS at -O3 (#5744)

* Re-amalgamate single_include after #5737

#5737 changed sources under include/ without regenerating single_include,
so the single header on develop is out of date. This commit only runs
make amalgamate.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Suppress GCC's false -Warray-bounds error in set_parents() with JSON_DIAGNOSTICS

With JSON_DIAGNOSTICS, GCC 13 to 15 report a false -Warray-bounds error
at -O3 when set_parents() is inlined right after a non-container value
(e.g., a string) was created: the std::map access in the object branch
is checked against the string allocation although m_type rules that
branch out. GCC 15 fixed the pattern from #4819 but not this one.

Suppress -Warray-bounds around set_parents() only, and add a regression
test that is compiled with -O3 -Werror=array-bounds on GCC.

Fixes #5742

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix the false -Warray-bounds error by setting the type after the value

The pragma around set_parents() is no longer needed: the base branch
(#5585) creates a value before setting its type. Before, the type of a new
string was stored first, so GCC had to assume that operator new could
change it again, kept the object branch of the inlined set_parents() alive,
and checked it against the string's allocation. Rewriting set_parents()
itself (if/else instead of switch) does not help.

Verified with Docker GCC 12.5, 13, 14, 15.3 and 16.2: the regression test
builds and passes without the pragma, and fails to build on develop.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-10-02 11:24:10 +02:00
committed by GitHub
parent 362a902170
commit dd2dd45abd
2 changed files with 85 additions and 0 deletions
+5
View File
@@ -139,6 +139,11 @@ json_test_set_test_options(test-unicode4 TEST_PROPERTIES TIMEOUT 3000)
# only the #972 regression test needs thirdparty/fifo_map on its include path
json_test_set_test_options(test-regression1 LINK_LIBRARIES fifo_map_include)
# GCC's false -Warray-bounds error with JSON_DIAGNOSTICS only shows up when optimizing (#5742)
json_test_set_test_options(test-diagnostics-optimized
COMPILE_OPTIONS $<$<CXX_COMPILER_ID:GNU>:-O3 -Werror=array-bounds>
)
#############################################################################
# add unit tests
#############################################################################
+80
View File
@@ -0,0 +1,80 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
// Regression test for https://github.com/nlohmann/json/issues/5742: with
// JSON_DIAGNOSTICS, GCC (12 to at least 16) reported a false -Warray-bounds
// error in the inlined set_parents() at -O3. The type of a new string was set
// before the string was allocated, so GCC had to assume that operator new
// could change it again and checked the object branch of set_parents()
// against the string's allocation. Setting the type after creating the value
// avoids this. The warning depends on GCC's inlining decisions, so the
// sections cover two patterns that trigger it on different GCC versions
// (#4819 and #5742).
// On GCC, this file is compiled with -O3 -Werror=array-bounds (see
// tests/CMakeLists.txt), so the test fails to build if the warning returns.
#include "doctest_compatibility.h"
#ifdef JSON_DIAGNOSTICS
#undef JSON_DIAGNOSTICS
#endif
#define JSON_DIAGNOSTICS 1
#include <nlohmann/json.hpp>
using nlohmann::json;
#include <algorithm>
#include <iterator>
#include <utility>
#include <vector>
namespace
{
enum class diag_color
{
red,
green,
blue
};
void to_json(json& j, const diag_color& c)
{
static const std::pair<diag_color, json> m[] = // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
{
{diag_color::red, "r"},
{diag_color::green, "g"},
{diag_color::blue, "b"},
};
const auto* it = std::find_if(std::begin(m), std::end(m), [c](const std::pair<diag_color, json>& p)
{
return p.first == c;
});
j = it->second;
}
} // namespace
TEST_CASE("diagnostics with optimization")
{
SECTION("issue #4819 - object in vector")
{
std::vector<json> jsons{};
jsons.emplace_back(json({{"key", "value"}}));
CHECK(jsons.back()["key"] == "value");
}
SECTION("issue #5742 - string values from a static table")
{
json j = json::array();
j.push_back(diag_color::red);
j.push_back(diag_color::green);
j.push_back(diag_color::blue);
CHECK(j.dump() == R"(["r","g","b"])");
CHECK_THROWS_WITH_AS(j[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
}
}