diff --git a/include/boost/sml.hpp b/include/boost/sml.hpp index 02d956f9..c0fed2a8 100644 --- a/include/boost/sml.hpp +++ b/include/boost/sml.hpp @@ -649,8 +649,13 @@ struct pool_type_impl::value && a constexpr explicit pool_type_impl(T &value) : value{value} {} template constexpr explicit pool_type_impl(TObject value) : value_{value}, value{value_} {} + // Copy-construct from another pool: extract T from the source pool into the + // local backing store, then bind the reference member to it. The old code + // used ': value(i, object)' which is a comma expression — it evaluated + // (i, object) and bound 'value' (T&) directly to the pool parameter + // 'object', leaving a dangling reference once the constructor returned. (#504) template - constexpr pool_type_impl(const init &i, const TObject &object) : value(i, object) {} + constexpr pool_type_impl(const init &, const TObject &object) : value_{try_get(&object)}, value{value_} {} T value_{}; T &value; }; diff --git a/test/ft/dependencies.cpp b/test/ft/dependencies.cpp index 112f1670..1e39bccd 100644 --- a/test/ft/dependencies.cpp +++ b/test/ft/dependencies.cpp @@ -353,3 +353,45 @@ test dependencies_multiple_subs = [] { } }; #endif + +// Issue #504: pool_type_impl's (init, object) constructor used a comma +// expression ': value(i, object)' to "initialize" a reference member. +// (i, object) is the C++ comma operator — it evaluates i, discards the result, +// then evaluates object, whose value becomes the initializer. For a T& member +// this bound the reference to the 'object' function parameter, which is a +// dangling reference after the constructor returns. +// Fix: initialize the backing store value_ via try_get, then bind value to value_. +// This test exercises the pool(const pool&) copy-constructor path that +// instantiates pool_type_impl(const init&, const TObject&). +struct dep504 { + int val = 99; +}; + +test ref_dep_copy_from_pool_not_dangling = [] { + // SM with a reference dep (dep504&) and a sub-SM so that the + // pool(const pool&) path is exercised during sm construction. + struct sub504 { + auto operator()() noexcept { + using namespace sml; + auto check = [](dep504& d) { expect(99 == d.val); }; + // clang-format off + return make_transition_table(*idle + event / check = X); + // clang-format on + } + }; + + struct top504 { + auto operator()() noexcept { + using namespace sml; + // clang-format off + return make_transition_table(*idle + event = sml::state); + // clang-format on + } + }; + + dep504 dep; + sml::sm sm{dep}; + sm.process_event(e1{}); + sm.process_event(e2{}); + expect(sm.is>(sml::X)); +};