Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 55 additions & 69 deletions include/boost/safe_numbers/detail/compile_assert.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
* output.
*/

// BOOST_SAFE_NUMBERS_COMPILE_ASSERT() takes an optional message: (expr) or (expr, "why").
#define BOOST_SAFE_NUMBERS_CA_FIRST(first, ...) first
#define BOOST_SAFE_NUMBERS_CA_MSG(...) BOOST_SAFE_NUMBERS_CA_MSG_(__VA_ARGS__, "")
#define BOOST_SAFE_NUMBERS_CA_MSG_(expression, message, ...) message

#ifdef __GNUC__

// The compile-time-error mechanism relies on the GCC/Clang function 'error' attribute.
Expand Down Expand Up @@ -75,7 +80,7 @@
* @def compile_assert
* @brief Macro for compile-time assertions.
* @param expression The compile-time condition to be checked.
* @param message A description of the assertion.
* @param message Optional description of the assertion; defaults to "" when omitted.
*/
#define BOOST_SAFE_NUMBERS_CA_ASSERT_IMPL(expression, message, fn) \
do { \
Expand All @@ -84,61 +89,60 @@
fn(); \
} \
} while (0)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT(expression, message) \
BOOST_SAFE_NUMBERS_CA_ASSERT_IMPL(expression, message, BOOST_SAFE_NUMBERS_CA_CAT(_compile_assert_fail_, __COUNTER__))

#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT(...) \
BOOST_SAFE_NUMBERS_CA_ASSERT_IMPL(BOOST_SAFE_NUMBERS_CA_FIRST(__VA_ARGS__), BOOST_SAFE_NUMBERS_CA_MSG(__VA_ARGS__), BOOST_SAFE_NUMBERS_CA_CAT(_compile_assert_fail_, __COUNTER__))

#define BOOST_SAFE_NUMBERS_CA_CONST_P_IMPL(expression, message, fn) \
do { \
if(__builtin_constant_p(expression)) { \
if (!(expression)) { \
void fn() __attribute__ ((error(message))); \
fn(); \
} \
} \
} while (0)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P(expression, message) \
BOOST_SAFE_NUMBERS_CA_CONST_P_IMPL(expression, message, BOOST_SAFE_NUMBERS_CA_CAT(_compile_assert_fail_, __COUNTER__))

// GCC before 13 does not fold __builtin_constant_p when it is applied to a value derived
// from an overflow builtin, or even to a comparison in a deep constexpr call chain, so the
// guard above is missed and the assertion never fires. Guarding instead on the constness of
// the raw operands (plain integers, which fold on every supported GCC) makes the check fire
// on GCC 11 and 12 as well; the condition is still evaluated by dead-code removal exactly as
// before. CONST_P1 guards one operand, CONST_P2 guards two.
#define BOOST_SAFE_NUMBERS_CA_CONST_P1_IMPL(a, condition, message, fn) \
do { \
if (__builtin_constant_p(a)) { \
if (!(condition)) { \
void fn() __attribute__ ((error(message))); \
fn(); \
} \
} \
} while (0)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P1(a, condition, message) \
BOOST_SAFE_NUMBERS_CA_CONST_P1_IMPL(a, condition, message, BOOST_SAFE_NUMBERS_CA_CAT(_compile_assert_fail_, __COUNTER__))

#define BOOST_SAFE_NUMBERS_CA_CONST_P2_IMPL(a, b, condition, message, fn) \
// CONST_P turns a provably-constant precondition violation into a build error. The optional
// trailing arguments name the operands that must be compile-time constants for the check to
// fire; with none given, the condition itself is the guarded operand:
//
// CONST_P(condition, message) guard on the condition
// CONST_P(condition, message, a) guard on operand a
// CONST_P(condition, message, a, b, ...) guard on every listed operand (ANDed)
//
// GCC before 13 does not fold __builtin_constant_p when it is applied to a value derived from
// an overflow builtin, or even to a comparison in a deep constexpr call chain, so a guard on
// the condition is missed and the assertion never fires. Naming the raw operands (plain
// integers, which fold on every supported GCC) makes the check fire on GCC 11 and 12 as well;
// the condition is still evaluated by dead-code removal exactly as before.

// __builtin_constant_p of every listed operand, ANDed together. The levels chain through
// distinct names so each re-expands cleanly; up to eight operands are supported.
#define BOOST_SAFE_NUMBERS_CA_G1(a, ...) __builtin_constant_p(a) __VA_OPT__(&& BOOST_SAFE_NUMBERS_CA_G2(__VA_ARGS__))
#define BOOST_SAFE_NUMBERS_CA_G2(a, ...) __builtin_constant_p(a) __VA_OPT__(&& BOOST_SAFE_NUMBERS_CA_G3(__VA_ARGS__))
#define BOOST_SAFE_NUMBERS_CA_G3(a, ...) __builtin_constant_p(a) __VA_OPT__(&& BOOST_SAFE_NUMBERS_CA_G4(__VA_ARGS__))
#define BOOST_SAFE_NUMBERS_CA_G4(a, ...) __builtin_constant_p(a) __VA_OPT__(&& BOOST_SAFE_NUMBERS_CA_G5(__VA_ARGS__))
#define BOOST_SAFE_NUMBERS_CA_G5(a, ...) __builtin_constant_p(a) __VA_OPT__(&& BOOST_SAFE_NUMBERS_CA_G6(__VA_ARGS__))
#define BOOST_SAFE_NUMBERS_CA_G6(a, ...) __builtin_constant_p(a) __VA_OPT__(&& BOOST_SAFE_NUMBERS_CA_G7(__VA_ARGS__))
#define BOOST_SAFE_NUMBERS_CA_G7(a, ...) __builtin_constant_p(a) __VA_OPT__(&& BOOST_SAFE_NUMBERS_CA_G8(__VA_ARGS__))
#define BOOST_SAFE_NUMBERS_CA_G8(a) __builtin_constant_p(a)
#define BOOST_SAFE_NUMBERS_CA_GUARD(...) BOOST_SAFE_NUMBERS_CA_G1(__VA_ARGS__)

// Pick the guard expression: the condition when no operands were given, else the ANDed operands.
#define BOOST_SAFE_NUMBERS_CA_CONST_P_GUARD_(condition, ...) __builtin_constant_p(condition)
#define BOOST_SAFE_NUMBERS_CA_CONST_P_GUARD_1(condition, ...) BOOST_SAFE_NUMBERS_CA_GUARD(__VA_ARGS__)
#define BOOST_SAFE_NUMBERS_CA_CONST_P_GUARD(condition, ...) \
BOOST_SAFE_NUMBERS_CA_CAT(BOOST_SAFE_NUMBERS_CA_CONST_P_GUARD_, __VA_OPT__(1))(condition, __VA_ARGS__)

#define BOOST_SAFE_NUMBERS_CA_CONST_P_IMPL(condition, message, guard, fn) \
do { \
if (__builtin_constant_p(a) && __builtin_constant_p(b)) { \
if (guard) { \
if (!(condition)) { \
void fn() __attribute__ ((error(message))); \
fn(); \
} \
} \
} while (0)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P2(a, b, condition, message) \
BOOST_SAFE_NUMBERS_CA_CONST_P2_IMPL(a, b, condition, message, BOOST_SAFE_NUMBERS_CA_CAT(_compile_assert_fail_, __COUNTER__))


#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT0(expression) BOOST_SAFE_NUMBERS_COMPILE_ASSERT(expression, NULL)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P(condition, message, ...) \
BOOST_SAFE_NUMBERS_CA_CONST_P_IMPL( \
condition, message, \
BOOST_SAFE_NUMBERS_CA_CONST_P_GUARD(condition, __VA_ARGS__), \
BOOST_SAFE_NUMBERS_CA_CAT(_compile_assert_fail_, __COUNTER__))

#else
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT(condition, description)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT0(expression)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P(expression, message)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P1(a, condition, message)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P2(a, b, condition, message)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT(...)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P(condition, message, ...)
#endif


Expand Down Expand Up @@ -219,22 +223,18 @@ int stop_compile3() __attribute__ ((error("'compile_assert_scalar error detected
#define BOOST_SAFE_NUMBERS_MERGE1(a,b) BOOST_SAFE_NUMBERS_MERGE2(a,b)
#define BOOST_SAFE_NUMBERS_MERGE3(a,b,c) BOOST_SAFE_NUMBERS_MERGE1(a, BOOST_SAFE_NUMBERS_MERGE1(b,c))

// The non-active fallback above already defined these as empty; replace them here.
// The non-active fallback above already defined this as empty
#undef BOOST_SAFE_NUMBERS_COMPILE_ASSERT
#undef BOOST_SAFE_NUMBERS_COMPILE_ASSERT0
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT(expr, message) \
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT(...) \
do { \
if (!(expr)) { \
if (!(BOOST_SAFE_NUMBERS_CA_FIRST(__VA_ARGS__))) { \
extern void BOOST_SAFE_NUMBERS_MERGE3(_compile_assert, BOOST_SAFE_NUMBERS_COMPILE_FILE, __LINE__)(); \
BOOST_SAFE_NUMBERS_MERGE3(_compile_assert, BOOST_SAFE_NUMBERS_COMPILE_FILE, __LINE__)(); \
} \
} while (0)

#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT0(expression) BOOST_SAFE_NUMBERS_COMPILE_ASSERT(expression, NULL)
#else

#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT(condition, description)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT0(expression)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT(...)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT_ACTIVE

#endif // defined(BOOST_SAFE_NUMBERS_ENABLE_COMPILE_ASSERT)
Expand All @@ -249,15 +249,13 @@ do { \
#if !defined(BOOST_SAFE_NUMBERS_COMPILE_ASSERT)
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT_ACTIVE

#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT(expression, message) \
#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT(...) \
do { \
void _compile_assert_fail(); \
if (!(expression)) { \
if (!(BOOST_SAFE_NUMBERS_CA_FIRST(__VA_ARGS__))) { \
_compile_assert_fail(); \
} \
} while (0)

#define BOOST_SAFE_NUMBERS_COMPILE_ASSERT0(expression) BOOST_SAFE_NUMBERS_COMPILE_ASSERT(expression, NULL)
#endif // !defined(compile_assert)
#endif // defined(BOOST_SAFE_NUMBERS_ENABLE_COMPILE_ASSERT)

Expand All @@ -266,20 +264,8 @@ do { \
#error "BOOST_SAFE_NUMBERS_COMPILE_ASSERT not defined"
#endif

#ifndef BOOST_SAFE_NUMBERS_COMPILE_ASSERT0
#error "BOOST_SAFE_NUMBERS_COMPILE_ASSERT0 not defined"
#endif

#ifndef BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P
#error "BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P not defined"
#endif

#ifndef BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P1
#error "BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P1 not defined"
#endif

#ifndef BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P2
#error "BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P2 not defined"
#endif

#endif // BOOST_SAFE_NUMBERS_DETAIL_COMPILE_ASSERT_HPP
2 changes: 1 addition & 1 deletion include/boost/safe_numbers/detail/signed_integer_basis.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2050,7 +2050,7 @@ struct signed_div_helper
#ifdef BOOST_SAFE_NUMBERS_ENABLE_COMPILE_ASSERT
// Divide-by-zero throws or exits under every policy that reaches this template
// (throw_exception, saturate, strict), so a constant zero divisor is always a bug.
BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P1(rhs_basis, rhs_basis != BasisType{0}, "signed division by zero");
BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P(rhs_basis != BasisType{0}, "signed division by zero", rhs_basis);

// min / -1 overflow is an error under throw_exception and strict; saturate returns max.
if constexpr (Policy == overflow_policy::throw_exception || Policy == overflow_policy::strict)
Expand Down
4 changes: 2 additions & 2 deletions include/boost/safe_numbers/detail/unsigned_integer_basis.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ struct add_helper
#ifdef BOOST_SAFE_NUMBERS_ENABLE_COMPILE_ASSERT
if constexpr (Policy == overflow_policy::throw_exception || Policy == overflow_policy::strict)
{
BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P2(lhs_basis, rhs_basis, !overflowed, "unsigned addition overflow");
BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P(!overflowed, "unsigned addition overflow", lhs_basis, rhs_basis);
}
#endif

Expand All @@ -614,7 +614,7 @@ struct add_helper
#ifdef BOOST_SAFE_NUMBERS_ENABLE_COMPILE_ASSERT
if constexpr (Policy == overflow_policy::throw_exception || Policy == overflow_policy::strict)
{
BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P2(lhs_basis, rhs_basis, !overflowed, "unsigned addition overflow");
BOOST_SAFE_NUMBERS_COMPILE_ASSERT_CONST_P(!overflowed, "unsigned addition overflow", lhs_basis, rhs_basis);
}
#endif

Expand Down
Loading