mirror of
https://github.com/ETLCPP/etl.git
synced 2026-10-28 18:07:10 +08:00
fix: prevent invalid variant visit dispatch
This commit is contained in:
parent
a8ab0bbeea
commit
d5839115e2
@ -55,7 +55,8 @@ namespace etl
|
||||
#elif defined(ETL_COMPILER_MICROSOFT)
|
||||
_ReadWriteBarrier();
|
||||
#else
|
||||
#error "Infinite loop not supported for this compiler and platform"
|
||||
volatile bool prevent_optimisation = true;
|
||||
static_cast<void>(prevent_optimisation);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ SOFTWARE.
|
||||
#include "../compare.h"
|
||||
#include "../error_handler.h"
|
||||
#include "../exception.h"
|
||||
#include "../infinite_loop.h"
|
||||
#include "../initializer_list.h"
|
||||
#include "../integral_limits.h"
|
||||
#include "../largest.h"
|
||||
@ -473,6 +474,17 @@ namespace etl
|
||||
|
||||
namespace private_variant
|
||||
{
|
||||
//***************************************************************************
|
||||
/// Reports an invalid visit and prevents execution from continuing if the
|
||||
/// configured error policy returns or runtime checks are disabled.
|
||||
//***************************************************************************
|
||||
ETL_NORETURN
|
||||
inline void invalid_visit()
|
||||
{
|
||||
ETL_ASSERT_FAIL(ETL_ERROR(bad_variant_access));
|
||||
etl::infinite_loop();
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
/// variant_base for non-trivially destructible types.
|
||||
/// Uses uninitialized_buffer (char array) storage and switch-based
|
||||
@ -2188,7 +2200,6 @@ namespace etl
|
||||
|
||||
//***************************************************************************
|
||||
/// Makes a call to TCallable using tIndex alternative to the variant.
|
||||
/// Instantiated as function pointer in the `do_visit` function.
|
||||
//***************************************************************************
|
||||
template <typename TRet, typename TCallable, typename TVariant, size_t tIndex>
|
||||
constexpr TRet do_visit_single(TCallable&& f, TVariant&& v)
|
||||
@ -2219,14 +2230,19 @@ namespace etl
|
||||
template <typename TRet, typename TCallable, typename TVariant, size_t... tIndices, typename... TVarRest>
|
||||
static ETL_CONSTEXPR14 TRet do_visit(TCallable&& f, TVariant&& v, index_sequence<tIndices...>, TVarRest&&... variants)
|
||||
{
|
||||
ETL_ASSERT(!v.valueless_by_exception(), ETL_ERROR(bad_variant_access));
|
||||
const size_t index = v.index();
|
||||
|
||||
if (index >= sizeof...(tIndices)) ETL_UNLIKELY
|
||||
{
|
||||
invalid_visit();
|
||||
}
|
||||
|
||||
using helper_t = do_visit_helper<TRet, TCallable, TVariant, TVarRest...>;
|
||||
using func_ptr = typename helper_t::function_pointer;
|
||||
|
||||
constexpr func_ptr jmp_table[]{helper_t::template fptr<tIndices>()...};
|
||||
|
||||
return jmp_table[v.index()](static_cast<TCallable&&>(f), static_cast<TVariant&&>(v), static_cast<TVarRest&&>(variants)...);
|
||||
return jmp_table[index](static_cast<TCallable&&>(f), static_cast<TVariant&&>(v), static_cast<TVarRest&&>(variants)...);
|
||||
}
|
||||
|
||||
template <typename TRet, typename TCallable, typename TVariant, typename... TVs>
|
||||
|
||||
@ -2548,6 +2548,31 @@ namespace
|
||||
CHECK_EQUAL(70, type);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_variant_visit_dispatches_every_alternative)
|
||||
{
|
||||
etl::variant<int8_t, uint8_t, int16_t> variant;
|
||||
variant_test_visit_dispatcher visitor;
|
||||
|
||||
variant = int8_t{};
|
||||
CHECK_EQUAL(5, etl::visit(visitor, variant));
|
||||
|
||||
variant = uint8_t{};
|
||||
CHECK_EQUAL(6, etl::visit(visitor, variant));
|
||||
|
||||
variant = int16_t{};
|
||||
CHECK_EQUAL(7, etl::visit(visitor, variant));
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_variant_visit_rejects_valueless_variant)
|
||||
{
|
||||
etl::variant<int8_t, uint8_t> variant(etl::private_variant::valueless_t{});
|
||||
variant_test_visit_dispatcher visitor;
|
||||
|
||||
CHECK_THROW(etl::visit(visitor, variant), etl::bad_variant_access);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
struct test_variant_multiple_visit_helper
|
||||
{
|
||||
@ -2590,6 +2615,15 @@ namespace
|
||||
CHECK_EQUAL(32, res);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_variant_multiple_visit_rejects_valueless_later_variant)
|
||||
{
|
||||
etl::variant<int8_t, uint8_t> variant1;
|
||||
etl::variant<int8_t, uint16_t, uint8_t> variant2(etl::private_variant::valueless_t{});
|
||||
|
||||
CHECK_THROW(etl::visit<int16_t>(test_variant_multiple_visit_helper{}, variant1, variant2), etl::bad_variant_access);
|
||||
}
|
||||
|
||||
#if ETL_USING_CPP14
|
||||
//*************************************************************************
|
||||
TEST(test_variant_multiple_visit_auto_return)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user