Fix undefined behaviour of etl::absolute() for etl::numeric_limits<T>::min (#1333)

absolute_unsigned() already handled that case correctly. Now adding this
check to absolute() also. It can be caught at compile time now, if in a
constexpr context. Also separating integral and non-integral versions
via enable_if.

Co-authored-by: John Wellbelove <jwellbelove@users.noreply.github.com>
This commit is contained in:
Roland Reichwein 2026-03-12 17:38:10 +01:00 committed by GitHub
parent 54a1c82c98
commit 0a56d40bdd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 30 additions and 4 deletions

View File

@ -34,16 +34,43 @@ SOFTWARE.
#include "platform.h"
#include "type_traits.h"
#include "integral_limits.h"
#include "error_handler.h"
namespace etl
{
namespace private_absolute
{
//*************************************************************************
// Non-constexpr function that is never called for valid inputs.
// If reached during constant evaluation, the compiler emits an error
// because it's not constexpr.
// At runtime, triggers the ETL assert handler.
//*************************************************************************
template <typename T>
inline T signed_min_error()
{
ETL_ASSERT_FAIL(ETL_ERROR_GENERIC("absolute value of minimum signed integer is undefined"));
return T(0);
}
}
//***************************************************************************
// For signed types.
//***************************************************************************
template <typename T>
ETL_NODISCARD
ETL_CONSTEXPR
typename etl::enable_if<etl::is_signed<T>::value, T>::type
ETL_CONSTEXPR
typename etl::enable_if<etl::is_signed<T>::value && etl::is_integral<T>::value, T>::type
absolute(T value)
{
return (value == etl::integral_limits<T>::min) ? etl::private_absolute::signed_min_error<T>()
: (value < T(0)) ? -value : value;
}
template <typename T>
ETL_NODISCARD
ETL_CONSTEXPR
typename etl::enable_if<etl::is_signed<T>::value && !etl::is_integral<T>::value, T>::type
absolute(T value) ETL_NOEXCEPT
{
return (value < T(0)) ? -value : value;

View File

@ -574,12 +574,11 @@ namespace
constexpr uint16_t absolute1 = etl::absolute(int16_t(0));
constexpr uint16_t absolute2 = etl::absolute(int16_t(32767));
constexpr uint16_t absolute3 = etl::absolute(int16_t(-32767));
constexpr uint16_t absolute4 = etl::absolute(int16_t(-32768));
//constexpr uint16_t absolute4 = etl::absolute(int16_t(-32768)); // Compile error
CHECK_EQUAL(uint16_t(0), absolute1);
CHECK_EQUAL(uint16_t(32767), absolute2);
CHECK_EQUAL(uint16_t(32767), absolute3);
CHECK_EQUAL(uint16_t(32768), absolute4);
constexpr uint16_t absolute5 = etl::absolute(uint16_t(0));
constexpr uint16_t absolute6 = etl::absolute(uint16_t(65535));