From c93c701e0af7bebd84cbe7d1a0da5d82bc6667f5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 22 Mar 2026 11:01:36 -1000 Subject: [PATCH] fixes, safety --- esphome/core/helpers.h | 27 ++++++--------------------- esphome/cpp_generator.py | 6 +++++- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 233fa481ed..c1e3c6eaf2 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -2228,33 +2228,18 @@ template U> T clamp_at_most(T value, * This struct is designed to replace dynamic heap allocations (`new T(...)`) for * global or static singletons within ESPHome, preventing memory fragmentation. * The underlying object must be explicitly constructed using placement new - * before access. + * before access. No destructor is called — this is intentional since ESPHome + * singletons live for the entire device lifetime. */ template struct PlacementStorage { /// @brief Raw byte storage, strictly aligned for type T. alignas(T) unsigned char data[sizeof(T)]; - /** - * @brief Safely retrieves a pointer to the constructed object. - * @return T* Pointer to the underlying object. - */ - constexpr T *get() { return reinterpret_cast(data); } + /// @brief Retrieves a pointer to the constructed object. + T *get() { return std::launder(reinterpret_cast(data)); } - /** - * @brief Safely retrieves a const pointer to the constructed object. - * @return const T* Const pointer to the underlying object. - */ - constexpr const T *get() const { return reinterpret_cast(data); } - - /// @brief Member access operator pointing to the underlying object. - constexpr T *operator->() { return get(); } - /// @brief Const member access operator pointing to the underlying object. - constexpr const T *operator->() const { return get(); } - - /// @brief Dereference operator yielding a reference to the underlying object. - constexpr T &operator*() { return *get(); } - /// @brief Const dereference operator yielding a const reference to the underlying object. - constexpr const T &operator*() const { return *get(); } + /// @brief Retrieves a const pointer to the constructed object. + const T *get() const { return std::launder(reinterpret_cast(data)); } }; /// @name Internal functions diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index af332243c5..3d34ff0eb3 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -594,7 +594,11 @@ def Pvariable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj": # for brace-enclosed initializer lists passed to variadic templates. call_str = rhs_str[4:] # Strip "new " from "new Type(args)" - the_type = id_.type if id_.type is not None else call_str.split("(")[0].strip() + the_type = ( + id_.type + if id_.type is not None + else call_str.split("(", maxsplit=1)[0].strip() + ) storage_name = f"{id_.id}_storage_" # Declare the static PlacementStorage