From 2bdf1d0e42749fdf37759d29c194e7827dc68bbb Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 16 Mar 2026 08:07:03 -1000 Subject: [PATCH] [core] Add std::launder, fix static_assert comment, document call() - Add std::launder to reinterpret_cast in inline callback path for formal C++20 correctness (generates identical code) - Fix static_assert comment: sizeof equality ensures round-trip through uintptr_t, not absence of trap representations per se - Document that call() is only valid on Callbacks from create() --- esphome/core/helpers.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index b98cb6a45b..2ac516281b 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -1737,13 +1737,15 @@ constexpr float fahrenheit_to_celsius(float value) { return (value - 32.0f) / 1. template struct Callback; template struct Callback { - // The inline storage path stores callable bytes in ctx via memcpy. - // This requires all bit patterns to be valid for void* (no trap representations). + // The inline storage path stores callable bytes in ctx_ via memcpy. + // sizeof equality with uintptr_t ensures void* can round-trip arbitrary bit patterns, + // which combined with flat address spaces on all ESPHome targets means no trap representations. static_assert(sizeof(void *) == sizeof(std::uintptr_t), "void* must be the same size as uintptr_t"); void (*fn_)(void *, Ts...){nullptr}; void *ctx_{nullptr}; + /// Invoke the callback. Only valid on Callbacks created via create(), never on default-constructed instances. void call(Ts... args) const { this->fn_(this->ctx_, args...); } /// Create from any callable. Small trivially-copyable callables (like [this] lambdas) @@ -1759,7 +1761,7 @@ template struct Callback { cb.fn_ = [](void *c, Ts... args) { alignas(DecayF) char buf[sizeof(DecayF)]; __builtin_memcpy(buf, &c, sizeof(DecayF)); - (*reinterpret_cast(buf))(args...); + (*std::launder(reinterpret_cast(buf)))(args...); }; return cb; } else {