diff --git a/include/chaiscript/dispatchkit/boxed_cast_helper.hpp b/include/chaiscript/dispatchkit/boxed_cast_helper.hpp index fc547b18..f730a83a 100644 --- a/include/chaiscript/dispatchkit/boxed_cast_helper.hpp +++ b/include/chaiscript/dispatchkit/boxed_cast_helper.hpp @@ -122,6 +122,9 @@ namespace chaiscript { template struct Cast_Helper_Inner { static Result &&cast(const Boxed_Value &ob, const Type_Conversions_State *) { + if (ob.is_ref()) { + throw chaiscript::detail::exception::bad_any_cast(); + } return std::move(*static_cast(verify_type(ob, typeid(Result), ob.get_ptr()))); } }; diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 644a847f..1d20474b 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -2206,3 +2206,79 @@ TEST_CASE("Exception from C++ [] operator is catchable in ChaiScript") { caught )") == true); } + +class Issue339_Object { +public: + Issue339_Object() = default; + Issue339_Object(const Issue339_Object &) { ++copy_count(); } + Issue339_Object(Issue339_Object &&) { ++move_count(); } + Issue339_Object &operator=(const Issue339_Object &) { + ++copy_assign_count(); + return *this; + } + Issue339_Object &operator=(Issue339_Object &&) { + ++move_assign_count(); + return *this; + } + + static int ©_count() { + static int c = 0; + return c; + } + static int &move_count() { + static int c = 0; + return c; + } + static int ©_assign_count() { + static int c = 0; + return c; + } + static int &move_assign_count() { + static int c = 0; + return c; + } + static void reset_counts() { + copy_count() = 0; + move_count() = 0; + copy_assign_count() = 0; + move_assign_count() = 0; + } +}; + +TEST_CASE("Issue #339 - lvalue reference should not implicitly move") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + + auto m = std::make_shared(); + m->add(chaiscript::user_type(), "Obj"); + m->add(chaiscript::constructor(), "Obj"); + m->add(chaiscript::constructor(), "Obj"); + m->add(chaiscript::constructor(), "Obj"); + m->add(chaiscript::fun(static_cast(&Issue339_Object::operator=)), "="); + m->add(chaiscript::fun(static_cast(&Issue339_Object::operator=)), "="); + m->add(chaiscript::fun([]() -> Issue339_Object & { + static Issue339_Object persistent; + return persistent; + }), + "getObj"); + m->add(chaiscript::fun([]() -> Issue339_Object { return Issue339_Object(); }), "makeObj"); + chai.add(m); + + SECTION("var from lvalue ref uses copy ctor, not move ctor on the source") { + Issue339_Object::reset_counts(); + chai.eval("var o = getObj()"); + CHECK(Issue339_Object::copy_count() >= 1); + } + + SECTION("assignment from lvalue ref uses copy assign, not move assign") { + Issue339_Object::reset_counts(); + chai.eval("var o2 = Obj(); o2 = getObj()"); + CHECK(Issue339_Object::copy_assign_count() > 0); + CHECK(Issue339_Object::move_assign_count() == 0); + } + + SECTION("return-by-value can still use move") { + Issue339_Object::reset_counts(); + chai.eval("var o3 = makeObj()"); + CHECK((Issue339_Object::copy_count() + Issue339_Object::move_count()) >= 1); + } +}