From 9d59f9ca7a571085cdc85ac8743e270711f7ca7d Mon Sep 17 00:00:00 2001 From: George Evmenov Date: Thu, 22 Oct 2026 05:04:00 +0300 Subject: [PATCH 1/2] Work around GCC 12 optional parsing crash with native tuple storage --- include/rfl/Tuple.hpp | 14 ++++----- tests/CMakeLists.txt | 10 ++++++ tests/regressions/gcc12_read_optional.cpp | 37 +++++++++++++++++++++++ 3 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 tests/regressions/gcc12_read_optional.cpp diff --git a/include/rfl/Tuple.hpp b/include/rfl/Tuple.hpp index c08a4c03e..ee12e54c2 100644 --- a/include/rfl/Tuple.hpp +++ b/include/rfl/Tuple.hpp @@ -36,7 +36,7 @@ class Tuple { static constexpr unsigned int num_bytes_ = std::get(positions_); - using DataType = std::array; + using DataType = unsigned char[num_bytes_]; public: /// Constructs a Tuple by copying from the provided arguments. @@ -68,7 +68,7 @@ class Tuple { template constexpr auto& get() { using Type = internal::nth_element_t<_index, Types...>; - return *internal::ptr_cast(data_.data() + pos<_index>()); + return *internal::ptr_cast(data_ + pos<_index>()); } /// Gets an element by index (const). @@ -77,7 +77,7 @@ class Tuple { template constexpr const auto& get() const { using Type = internal::nth_element_t<_index, Types...>; - return *internal::ptr_cast(data_.data() + pos<_index>()); + return *internal::ptr_cast(data_ + pos<_index>()); } /// Copy assignment operator. Assigns from another Tuple by copy. @@ -157,7 +157,7 @@ class Tuple { const auto copy_one = [this](const auto& _other, std::integral_constant) { using Type = internal::nth_element_t<_i, Types...>; - ::new (static_cast(data_.data() + pos<_i>())) + ::new (static_cast(data_ + pos<_i>())) Type(_other.template get<_i>()); }; (copy_one(_other, std::integral_constant{}), ...); @@ -172,7 +172,7 @@ class Tuple { const auto copy_one = [this](const auto& _t, std::integral_constant) { using Type = internal::nth_element_t<_i, Types...>; - ::new (static_cast(data_.data() + pos<_i>())) Type(_t); + ::new (static_cast(data_ + pos<_i>())) Type(_t); }; (copy_one(_types, std::integral_constant{}), ...); } @@ -198,7 +198,7 @@ class Tuple { const auto move_one = [this](auto&& _other, std::integral_constant) { using Type = internal::nth_element_t<_i, Types...>; - ::new (static_cast(data_.data() + pos<_i>())) + ::new (static_cast(data_ + pos<_i>())) Type(std::move(_other.template get<_i>())); }; (move_one(_other, std::integral_constant{}), ...); @@ -212,7 +212,7 @@ class Tuple { const auto move_one = [this](auto&& _t, std::integral_constant) { using Type = internal::nth_element_t<_i, Types...>; - ::new (static_cast(data_.data() + pos<_i>())) Type(std::move(_t)); + ::new (static_cast(data_ + pos<_i>())) Type(std::move(_t)); }; (move_one(std::move(_types), std::integral_constant{}), ...); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 553a5088c..4dcc7053d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,16 @@ if (REFLECTCPP_JSON) add_subdirectory(json_c_arrays_and_inheritance) add_subdirectory(cli) add_subdirectory(env) + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # Issue #719 requires GCC, internal linkage, and -O2. The ordinary JSON + # suite uses -O3, which hides this regression. + add_executable(reflect-cpp-gcc12-optional-tests + regressions/gcc12_read_optional.cpp) + target_link_libraries(reflect-cpp-gcc12-optional-tests PRIVATE reflectcpp) + target_compile_options(reflect-cpp-gcc12-optional-tests PRIVATE + -O2 -fstrict-aliasing) + add_test(NAME gcc12_read_optional COMMAND reflect-cpp-gcc12-optional-tests) + endif() endif () if (REFLECTCPP_AVRO) diff --git a/tests/regressions/gcc12_read_optional.cpp b/tests/regressions/gcc12_read_optional.cpp new file mode 100644 index 000000000..b60bb9dac --- /dev/null +++ b/tests/regressions/gcc12_read_optional.cpp @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include + +// Keep both types in an anonymous namespace: external linkage hides the +// GCC 12.5 -O2 regression from issue #719. +namespace { + +struct Inner { + std::optional a; + std::optional b; +}; + +struct Outer { + std::optional f1; + std::optional f2; + std::optional f3; + std::optional f4; + std::optional f5; + std::optional f6; + std::optional f7; + std::optional f8; + std::optional f9; + std::optional f10; + std::optional f11; + std::optional inner; +}; + +} // namespace + +int main() { + const auto res = rfl::json::read("{}"); + std::cout << (res ? "OK" : "parse error") << std::endl; + return res ? 0 : 1; +} From dd51f93988f1a4b12e5cb01656fdc11910dc1627 Mon Sep 17 00:00:00 2001 From: George Evmenov Date: Thu, 22 Oct 2026 05:11:54 +0300 Subject: [PATCH 2/2] Remove standalone regression test from storage workaround --- tests/CMakeLists.txt | 10 ------ tests/regressions/gcc12_read_optional.cpp | 37 ----------------------- 2 files changed, 47 deletions(-) delete mode 100644 tests/regressions/gcc12_read_optional.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4dcc7053d..553a5088c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,16 +16,6 @@ if (REFLECTCPP_JSON) add_subdirectory(json_c_arrays_and_inheritance) add_subdirectory(cli) add_subdirectory(env) - if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - # Issue #719 requires GCC, internal linkage, and -O2. The ordinary JSON - # suite uses -O3, which hides this regression. - add_executable(reflect-cpp-gcc12-optional-tests - regressions/gcc12_read_optional.cpp) - target_link_libraries(reflect-cpp-gcc12-optional-tests PRIVATE reflectcpp) - target_compile_options(reflect-cpp-gcc12-optional-tests PRIVATE - -O2 -fstrict-aliasing) - add_test(NAME gcc12_read_optional COMMAND reflect-cpp-gcc12-optional-tests) - endif() endif () if (REFLECTCPP_AVRO) diff --git a/tests/regressions/gcc12_read_optional.cpp b/tests/regressions/gcc12_read_optional.cpp deleted file mode 100644 index b60bb9dac..000000000 --- a/tests/regressions/gcc12_read_optional.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include -#include -#include -#include -#include - -// Keep both types in an anonymous namespace: external linkage hides the -// GCC 12.5 -O2 regression from issue #719. -namespace { - -struct Inner { - std::optional a; - std::optional b; -}; - -struct Outer { - std::optional f1; - std::optional f2; - std::optional f3; - std::optional f4; - std::optional f5; - std::optional f6; - std::optional f7; - std::optional f8; - std::optional f9; - std::optional f10; - std::optional f11; - std::optional inner; -}; - -} // namespace - -int main() { - const auto res = rfl::json::read("{}"); - std::cout << (res ? "OK" : "parse error") << std::endl; - return res ? 0 : 1; -}