/* * This file is part of the DXX-Rebirth project . * It is copyright by its individual contributors, as recorded in the * project's Git history. See COPYING.txt at the top level for license * terms and a link to the Git history. */ #pragma once #include #include #include #include "fwd-valptridx.h" #include "pack.h" #include "compiler-poison.h" #include "selfiter.h" #ifdef DXX_CONSTANT_TRUE #define DXX_VALPTRIDX_STATIC_CHECK(SUCCESS_CONDITION,FAILURE_FUNCTION,FAILURE_STRING) \ static_cast(DXX_CONSTANT_TRUE(!SUCCESS_CONDITION) && \ (DXX_ALWAYS_ERROR_FUNCTION(FAILURE_FUNCTION, FAILURE_STRING), 0) \ ) \ #ifdef DXX_HAVE_ATTRIBUTE_WARNING /* This causes many warnings because some conversions are not checked for * safety. Eliminating the warnings by changing the call sites to check first * would be a useful improvement. */ //#define DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT __attribute__((__warning__("call not eliminated"))) #endif #else #define DXX_VALPTRIDX_STATIC_CHECK(E,F,S) #endif #define DXX_VALPTRIDX_CHECK(SUCCESS_CONDITION,ERROR,FAILURE_STRING,...) \ ( DXX_BEGIN_COMPOUND_STATEMENT { \ const bool dxx_valptridx_check_success_condition = (SUCCESS_CONDITION); \ DXX_VALPTRIDX_STATIC_CHECK(dxx_valptridx_check_success_condition, dxx_trap_##ERROR, FAILURE_STRING); \ static_cast( \ dxx_valptridx_check_success_condition || (ERROR::report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VA(__VA_ARGS__)), 0) \ ); \ } DXX_END_COMPOUND_STATEMENT ) #ifndef DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT #define DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT #endif template class valptridx::array_base_count_type { protected: union { unsigned count; /* * Use DXX_VALPTRIDX_FOR_EACH_PPI_TYPE to generate empty union * members based on basic_{i,v}val_member_factory * specializations. */ #define DXX_VALPTRIDX_DEFINE_MEMBER_FACTORIES(MANAGED_TYPE, DERIVED_TYPE_PREFIX, CONTEXT, PISUFFIX, IVPREFIX, MCPREFIX) \ valptridx::f ## IVPREFIX ## MCPREFIX ## PISUFFIX \ IVPREFIX ## MCPREFIX ## PISUFFIX DXX_VALPTRIDX_FOR_EACH_PPI_TYPE(DXX_VALPTRIDX_DEFINE_MEMBER_FACTORIES, managed_type,,); #undef DXX_VALPTRIDX_DEFINE_MEMBER_FACTORIES }; constexpr array_base_count_type() : count(0) { } public: unsigned get_count() const { return count; } void set_count(const unsigned c) { count = c; } }; template < typename INTEGRAL_TYPE, std::size_t array_size_value, valptridx_detail::untyped_utilities::report_error_style report_const_error_value, valptridx_detail::untyped_utilities::report_error_style report_mutable_error_value > constexpr std::integral_constant valptridx_detail::specialized_type_parameters::array_size; class valptridx_detail::untyped_utilities::report_error_undefined { public: __attribute_cold DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT static void report(...) { } }; class valptridx_detail::untyped_utilities::report_error_trap_terse { public: /* Accept and discard any arguments, to encourage the compiler to discard * as dead any values that exist only as arguments to `report()`. */ [[noreturn]] __attribute_cold DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT static void report(...) { __builtin_trap(); } }; class valptridx_detail::untyped_utilities::index_mismatch_trap_verbose { public: [[noreturn]] __attribute_cold DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const void *const array, const unsigned long supplied_index, const void *const expected_pointer, const void *const actual_pointer) { /* Load each of the arguments into storage before executing the trap, * so that inspection of those locations in the core dump can readily * retrieve these values, even if the values would otherwise be unused * and optimized out. */ __asm__ __volatile__("" :: DXX_VALPTRIDX_REPORT_STANDARD_ASM_LOAD_COMMA_R_VARS "rm" (array), "rm" (supplied_index), "rm" (expected_pointer), "rm" (actual_pointer)); __builtin_trap(); } }; class valptridx_detail::untyped_utilities::index_range_trap_verbose { public: [[noreturn]] __attribute_cold DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const void *const array, const unsigned long supplied_index) { __asm__ __volatile__("" :: DXX_VALPTRIDX_REPORT_STANDARD_ASM_LOAD_COMMA_R_VARS "rm" (array), "rm" (supplied_index)); __builtin_trap(); } }; class valptridx_detail::untyped_utilities::null_pointer_trap_verbose { public: [[noreturn]] __attribute_cold DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_DEFN_VARS) { __asm__ __volatile__("" :: DXX_VALPTRIDX_REPORT_STANDARD_ASM_LOAD_COMMA_N_VARS); __builtin_trap(); } [[noreturn]] __attribute_cold DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const void *const array) { __asm__ __volatile__("" :: DXX_VALPTRIDX_REPORT_STANDARD_ASM_LOAD_COMMA_R_VARS "rm" (array)); __builtin_trap(); } }; template class valptridx

::index_mismatch_exception : public std::logic_error { using std::logic_error::logic_error; public: [[noreturn]] __attribute_cold DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const array_managed_type *, index_type, const_pointer_type, const_pointer_type); }; template class valptridx

::index_range_exception : public std::out_of_range { using std::out_of_range::out_of_range; public: [[noreturn]] __attribute_cold DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const array_managed_type *, long); }; template class valptridx

::null_pointer_exception : public std::logic_error { using std::logic_error::logic_error; public: [[noreturn]] __attribute_cold DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_DEFN_VARS); [[noreturn]] __attribute_cold DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const array_managed_type *); }; template template void valptridx::check_index_match(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const_reference_type r, index_type i, const array_managed_type &a __attribute_unused) { const auto pi = &a[i]; DXX_VALPTRIDX_CHECK(pi == &r, handle_index_mismatch, "pointer/index mismatch", &a, i, pi, &r); } template template class Compare> typename valptridx::index_type valptridx::check_index_range(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const index_type i, const array_managed_type *const a) { std::size_t si; if constexpr (std::is_enum::value) si = static_cast(i); else si = i; check_index_range_size(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS si, a); return i; } template template class Compare> typename valptridx::index_type valptridx::check_index_range_size(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const std::size_t s, const array_managed_type *const a) { DXX_VALPTRIDX_CHECK(Compare()(s, array_size), handle_index_range_error, "invalid index used in array subscript", a, s); /* This cast will truncate the value to fit in index_type, which is * normally smaller than std::size_t. However, truncation is legal * here, because (1) DXX_VALPTRIDX_CHECK would trap on an index that * was outside the array size and (2) index_type can represent any * valid size in the array. Thus, if DXX_VALPTRIDX_CHECK did not * trap[1], the truncation cannot change the value of the index. * * [1] If valptridx was built without index validation, then no trap * would be issued even for invalid data. Validation-disabled builds * are permitted to exhibit undefined behavior in cases where the * validation-enabled build would have trapped. */ return static_cast(s); } template template void valptridx::check_null_pointer_conversion(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const_pointer_type p) { DXX_VALPTRIDX_CHECK(p, handle_null_pointer, "NULL pointer converted"); } template template void valptridx::check_null_pointer(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const_pointer_type p, const array_managed_type &a __attribute_unused) { DXX_VALPTRIDX_CHECK(p, handle_null_pointer, "NULL pointer used", &a); } template template void valptridx::check_implicit_index_range_ref(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const managed_type &r, const array_managed_type &a) { check_explicit_index_range_ref(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS r, static_cast(&r) - static_cast(&a.front()), a); } template template void valptridx::check_explicit_index_range_ref(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const_reference_type &r, std::size_t i, const array_managed_type &a) { const auto ii = check_index_range_size(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, &a); check_index_match(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS r, ii, a); } template class valptridx::partial_policy::require_valid { public: static constexpr bool allow_nullptr = false; [[nodiscard]] static constexpr std::false_type check_allowed_invalid_index(index_type) { return {}; } [[nodiscard]] static constexpr bool check_nothrow_index(index_type i) { return std::less()(static_cast(i), array_size); } }; template class valptridx::partial_policy::allow_invalid { public: static constexpr bool allow_nullptr = true; [[nodiscard]] static constexpr bool check_allowed_invalid_index(index_type i) { return i == static_cast(~0); } [[nodiscard]] static constexpr bool check_nothrow_index(index_type i) { return check_allowed_invalid_index(i) || require_valid::check_nothrow_index(i); } }; template constexpr bool valptridx::partial_policy::require_valid::allow_nullptr; template constexpr bool valptridx::partial_policy::allow_invalid::allow_nullptr; template template