Open congard opened 9 months ago
So, I've created a test case that reproduces this issue demonstrates UB of #1523. It seems that we are dealing with undefined behavior here - the test case produces slightly different output on each run.
Short test case description:
Foo
) and derived (Bar
)n
environments with the global environment as the parentFoo
and Bar
for the current environmentFoo
) as an argumentfunc
that takes an argument arg
and calls the aforementioned function with the argument arg
func
with a pointer to Bar
instance as an argumentCode:
#include <iostream>
#include <sol/sol.hpp>
//#define VERBOSE
#ifdef VERBOSE
#define LOG_V(...) printf(__VA_ARGS__)
#else
#define LOG_V(...)
#endif
// base class
class Foo {
public:
Foo() {
LOG_V("ctor\n");
}
~Foo() {
LOG_V("dtor\n");
}
};
// derived class
class Bar: public Foo {};
template<typename T> static void registerType(sol::table &table);
template<> void registerType<Foo>(sol::table &table) {
if (table["Foo"].valid())
return;
auto foo = table.new_usertype<Foo>("Foo");
LOG_V("Foo registered\n");
}
template<> void registerType<Bar>(sol::table &table) {
if (table["Bar"].valid())
return;
auto bar = table.new_usertype<Bar>("Bar", sol::base_classes, sol::bases<Foo>());
LOG_V("Bar registered\n");
}
static bool testEnvCount(int envCount) {
sol::state state;
std::vector<sol::environment> envs(envCount);
// create environments
for (auto &env : envs)
env = sol::environment(state, sol::create, state.globals());
auto cfgEnv = [&state](sol::environment &env) {
registerType<Bar>(env);
registerType<Foo>(env);
state.script("function func(foo) doSomethingWithFoo(foo) end", env);
env["doSomethingWithFoo"] = [](Foo *foo) {
LOG_V("[doSomethingWithFoo] Foo: %ti\n", (ptrdiff_t) foo);
};
};
// configure environments
for (auto &env : envs)
cfgEnv(env);
Bar bar;
auto callFunc = [bar_ptr = &bar](sol::environment &env) {
auto result = env["func"].get<sol::function>()(bar_ptr);
if (!result.valid()) {
sol::error error = result;
LOG_V("%s\n", error.what());
}
return result.valid();
};
// call Lua function "func"
for (auto &env : envs) {
if (!callFunc(env)) {
return false;
}
}
return true;
}
int main() {
constexpr int testFrom = 1;
constexpr int testTo = 30;
for (int i = testFrom; i <= testTo; ++i) {
if (testEnvCount(i)) {
std::cout << "OK for envCount = " << i << "\n";
} else {
std::cout << "Failed for envCount = " << i << "\n";
}
}
return 0;
}
Output:
Sol error message:
stack index 1, expected userdata, received sol.Bar *: value at this index does not properly reflect the desired type (ba
d argument into 'void(Foo *)')
stack traceback:
[C]: in global 'doSomethingWithFoo'
[string "function func(foo) doSomethingWithFoo(foo) en..."]:1: in function <[string "function func(foo) doSometh
ingWithFoo(foo) en..."]:1>
In the example above, switching to v3.2.3 helps, as it described in #1523. However, unfortunately, in my project it didn't help. The only available "workaround" is the one described at the end of the first comment.
Some additional debug info from my project (tested with sol v3.2.2):
algine::Object registered
algine::Scene (extends public algine::Object) registered
stack_check_unqualified.hpp:520: has_derived == false <------ error happens because of this
stack_check_unqualified.hpp:520: detail::demangle<T>() == "void) [T = algine::Object]"
stack_check_unqualified.hpp:536: success == false => calling `handler(L, index, type::userdata, indextype, "value at this index does not properly reflect the desired type")`
Error message:
stack index 1, expected userdata, received sol.void) [T = algine::Scene *]: value at this index does not properly reflect the desired type (bad argument into 'void) [T = void](void) [T = algine::Object *])')
stack traceback:
[C]: in function 'func'
[string "loadType('algine::Object') loadType('algine::..."]:1: in function 'f'
Code:
Lua lua;
auto &state = *lua.state();
sol::environment env = state.globals();
env["func"] = [](Object *obj) { printf("Global state\n"); };
state.script("loadType('algine::Object') loadType('algine::Scene') function f(o) func(o) end", env);
if (auto res = env["f"].get<sol::function>()((algine::Scene*) this); !res.valid()) {
sol::error err = res;
printf("%s\n", err.what());
}
Hope it will help to fix this issue
So, I continued my research regarding the original issue - and finally have found out all circumstances for the original issue aka "strange problem" to occur.
sol::state
is being used in the external libraryDemo and more detailed results can be found here: https://github.com/congard/sol2-issue-1532
Note: it is not a duplicate of #700
sol version: 3.3.0 Lua version: 5.4.4 Compiler:
Fedora 38 clang 16.0.0/ Windows 11 clang 16.0.0, target: x86_64-pc-windows-msvc Complete test case: https://github.com/ThePhD/sol2/issues/1532#issuecomment-1738995821I've bumped into quite strange sol2 behavior, that looks quite similar to #700 except one important thing - I have explicitly provided base classes. So, here is some of my code:
Base class binding:
Derived class binding:
Then, I create a new environment:
Finally, I register the aforementioned types like this:
My Lua code looks like:
doSomethingWithObject
implementation:And I get the following error:
What's strange, to make it work as expected I just need to create a new usertype that inherits
algine::Object
:So, my first question is: what can be wrong with the snippets above? What's the possible problem with them? What potential problems do you see? And my second question: is it safe to use
sol::table
instead ofsol::environment
in the bindings code?