diff --git a/include/boost/pfr/detail/core14_classic.hpp b/include/boost/pfr/detail/core14_classic.hpp index 6f83b2a..5d2399f 100644 --- a/include/boost/pfr/detail/core14_classic.hpp +++ b/include/boost/pfr/detail/core14_classic.hpp @@ -547,6 +547,10 @@ constexpr bool is_flat_refelectable(std::index_sequence) noexcept { template auto tie_as_flat_tuple(T& lvalue) noexcept { + static_assert( + !std::is_union::value, + "For safety reasons it is forbidden to flat_ reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + ); using type = std::remove_cv_t; using tuple_type = internal_tuple_with_same_alignment_t; @@ -558,9 +562,12 @@ auto tie_as_flat_tuple(T& lvalue) noexcept { template auto tie_as_tuple(T& val) noexcept { - typedef T type; static_assert( - boost::pfr::detail::is_flat_refelectable( std::make_index_sequence()>{} ), + !std::is_union::value, + "For safety reasons it is forbidden to reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + ); + static_assert( + boost::pfr::detail::is_flat_refelectable( std::make_index_sequence()>{} ), "Not possible in C++14 to represent that type without loosing information. Use boost::pfr::flat_ version, or change type definition, or enable C++17" ); return boost::pfr::detail::tie_as_flat_tuple(val); @@ -662,6 +669,11 @@ void for_each_field_dispatcher_1(T& t, F&& f, std::index_sequence, std::fa template void for_each_field_dispatcher(T& t, F&& f, std::index_sequence) { + static_assert( + !std::is_union::value, + "For safety reasons it is forbidden to reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + ); + /// Compile time error at this point means that you have called `for_each_field` or some other non-flat function or operator for a /// type that is not constexpr aggregate initializable. /// diff --git a/include/boost/pfr/detail/core14_loophole.hpp b/include/boost/pfr/detail/core14_loophole.hpp index 7a7ad15..007e354 100644 --- a/include/boost/pfr/detail/core14_loophole.hpp +++ b/include/boost/pfr/detail/core14_loophole.hpp @@ -139,7 +139,16 @@ decltype(auto) tie_or_value(T& val, std::enable_if_t -decltype(auto) tie_or_value(T& val, std::enable_if_t >::value && !std::is_enum< std::remove_reference_t >::value>* = 0) noexcept { +auto tie_or_value(T& val, std::enable_if_t >::value>* = 0) noexcept { + static_assert( + sizeof(T) && false, + "For safety reasons it is forbidden to flat_ reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + ); + return 0; +} + +template +decltype(auto) tie_or_value(T& val, std::enable_if_t >::value && !std::is_enum< std::remove_reference_t >::value && !std::is_union< std::remove_reference_t >::value>* = 0) noexcept { return val; } @@ -166,6 +175,10 @@ auto tie_as_tuple_recursively(rvalue_t tup) noexcept { template auto tie_as_flat_tuple(T& t) { + static_assert( + !std::is_union::value, + "For safety reasons it is forbidden to flat_ reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + ); auto rec_tuples = boost::pfr::detail::tie_as_tuple_recursively( boost::pfr::detail::tie_as_tuple_loophole_impl(t) ); @@ -179,6 +192,10 @@ auto tie_as_flat_tuple(T& t) { #if !BOOST_PFR_USE_CPP17 template auto tie_as_tuple(T& val) noexcept { + static_assert( + !std::is_union::value, + "For safety reasons it is forbidden to reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + ); return boost::pfr::detail::tie_as_tuple_loophole_impl( val ); @@ -186,6 +203,10 @@ auto tie_as_tuple(T& val) noexcept { template void for_each_field_dispatcher(T& t, F&& f, std::index_sequence) { + static_assert( + !std::is_union::value, + "For safety reasons it is forbidden to reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + ); std::forward(f)( boost::pfr::detail::tie_as_tuple_loophole_impl(t) ); diff --git a/include/boost/pfr/detail/core17.hpp b/include/boost/pfr/detail/core17.hpp index 3c9c8d3..ffd9611 100644 --- a/include/boost/pfr/detail/core17.hpp +++ b/include/boost/pfr/detail/core17.hpp @@ -48,6 +48,10 @@ static_assert( template void for_each_field_dispatcher(T& t, F&& f, std::index_sequence) { + static_assert( + !std::is_union::value, + "For safety reasons it is forbidden to reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + ); std::forward(f)( detail::tie_as_tuple(t) ); diff --git a/include/boost/pfr/detail/core17_generated.hpp b/include/boost/pfr/detail/core17_generated.hpp index 08d0f54..2e88488 100644 --- a/include/boost/pfr/detail/core17_generated.hpp +++ b/include/boost/pfr/detail/core17_generated.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2016-2017 Antony Polukhin +// Copyright (c) 2016-2018 Antony Polukhin // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -1028,6 +1028,10 @@ constexpr auto tie_as_tuple(T& val, size_t_<100>) noexcept { template constexpr auto tie_as_tuple(T& val) noexcept { + static_assert( + !std::is_union::value, + "For safety reasons it is forbidden to reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + ); typedef size_t_()> fields_count_tag; return boost::pfr::detail::tie_as_tuple(val, fields_count_tag{}); } diff --git a/misc/generate_cpp17.py b/misc/generate_cpp17.py index 279e6d2..773026f 100644 --- a/misc/generate_cpp17.py +++ b/misc/generate_cpp17.py @@ -13,7 +13,7 @@ import string # Skipping some letters that may produce keywords or are hard to read, or shadow template parameters ascii_letters = string.ascii_letters.replace("o", "").replace("O", "").replace("i", "").replace("I", "").replace("T", "") -PROLOGUE = """// Copyright (c) 2016-2017 Antony Polukhin +PROLOGUE = """// Copyright (c) 2016-2018 Antony Polukhin // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -68,6 +68,10 @@ EPILOGUE = """ template constexpr auto tie_as_tuple(T& val) noexcept { + static_assert( + !std::is_union::value, + "For safety reasons it is forbidden to reflect unions. It could lead to crashes (for example when attempting to output the union with inactive first `const char*` field)." + ); typedef size_t_()> fields_count_tag; return boost::pfr::detail::tie_as_tuple(val, fields_count_tag{}); } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 6750d42..1157025 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -62,6 +62,11 @@ test-suite pfr [ compile-fail common/virtual_functions.cpp : $(CLASSIC_PREC_DEF) : precise_virtual_functions ] [ compile-fail common/virtual_functions.cpp : $(LOOPHOLE_PREC_DEF) : precise_lh_virtual_functions ] + [ compile-fail common/ops_on_union.cpp : $(CLASSIC_FLAT_DEF) : flat_on_union ] + [ compile-fail common/ops_on_union.cpp : $(LOOPHOLE_FLAT_DEF) : flat_lh_on_union ] + [ compile-fail common/ops_on_union.cpp : $(CLASSIC_PREC_DEF) : precise_on_union ] + [ compile-fail common/ops_on_union.cpp : $(LOOPHOLE_PREC_DEF) : precise_lh_on_union ] + [ compile-fail common/ops_unions.cpp : $(CLASSIC_FLAT_DEF) : flat_unions ] [ compile-fail common/ops_unions.cpp : $(LOOPHOLE_FLAT_DEF) : flat_lh_unions ] [ compile-fail common/ops_unions.cpp : $(CLASSIC_PREC_DEF) : precise_unions ] diff --git a/test/common/ops_on_union.cpp b/test/common/ops_on_union.cpp new file mode 100644 index 0000000..37183ff --- /dev/null +++ b/test/common/ops_on_union.cpp @@ -0,0 +1,26 @@ +// Copyright (c) 2018 Antony Polukhin +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#ifdef BOOST_PFR_TEST_FLAT +#include +using namespace boost::pfr::flat_ops; +#endif + +#ifdef BOOST_PFR_TEST_PRECISE +#include +using namespace boost::pfr::ops; +#endif + +union test_union { + const char* c; + int i; +}; + +int main() { + test_union v{""}; + return v == v; +}