https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109732
Bug ID: 109732
Summary: [14 regression] gcc miscompiles iterator comparison on
nlohmann_json
Product: gcc
Version: 14.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: slyfox at gcc dot gnu.org
Target Milestone: ---
Initially I observed the failure as a test failure on nlohmann_json-3.11.2
against gcc-14 master (r14-395-g1adb1a653d6739):
33 - test-items_cpp11 (Failed)
34 - test-items_cpp17 (Failed)
I extracted smaller but not yet self-contained example that seems to illustrate
the problem:
// $ cat unit-t.cpp
#include <cstdio>
#include <nlohmann/json.hpp>
namespace {
int seen_failures = 0;
__attribute__((noinline))
static void sne(nlohmann::json::const_reverse_iterator lhs,
nlohmann::json::const_reverse_iterator rhs) {
bool res = !(lhs == rhs);
if (!res) seen_failures++;
}
struct TestCase
{
void (*m_test)();
TestCase(void (*test)()) {
// not used anywhere, but triggers the failure
m_test = test;
}
};
static void _DOCTEST_ANON_FUNC_8()
{
const nlohmann::json js = "hello world";
const nlohmann::json js_const(js);
nlohmann::json::const_reverse_iterator sit = js_const.crbegin();
sne(sit, js_const.crend());
}
}
int main() {
// below 3 lines look like a no-op, but afects the result:
TestCase ltc(&_DOCTEST_ANON_FUNC_8);
std::vector<const TestCase*> testArray;
testArray.push_back(<c);
_DOCTEST_ANON_FUNC_8();
puts((seen_failures > 0) ? "FAILURE!" : "SUCCESS!");
return EXIT_SUCCESS;
}
To trigger it we will need json headers-only library:
$ git clone --depth 1 https://github.com/nlohmann/json.git
# commit 6af826d0bdb55e4b69e3ad817576745335f243ca
$ g++-14 unit-t.cpp -O2 -Ijson/include -o a && ./a
FAILURE!
For comparison unoptimized and older gcc does work as expcted:
$ g++-14 unit-t.cpp -O0 -Ijson/include -o a && ./a
SUCCESS!
$ g++ unit-t.cpp -O2 -Ijson/include -o a && ./a
SUCCESS!
I'm not sure if `nlohmann::json::const_reverse_iterator` is implemented
correctly according to c++ requirements. The inheritance looks fishy. At least
I would expect consistent behaviour for -O0/O2.
-fsanitize={address,undefined} does not uncover anything obvious. Can you help
me understand if it's a gcc or json deficiency?
Thank you!