-
Kirill Smelkov authored
On Android it was discovered that e.g. test_pyx_errors_unwrap_cpp fails: __________________________________ test_pyx_errors_unwrap_cpp __________________________________ func = <cyfunction test_errors_unwrap_cpp at 0x75ee34e740> def _(func=getattr(mod, f)): > func() golang/golang_test.py:66: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ golang/_errors_test.pyx:61: in golang._errors_test.test_errors_unwrap_cpp _test_errors_unwrap_cpp() golang/_golang.pyx:77: in golang._golang.topyexc pypanic(pyarg) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ > raise _PanicError(arg) E golang._golang._PanicError: golang/errors_test.cpp:76: errors::Unwrap(err2) E have: 'nil' E want: 'zzz' golang/_golang.pyx:61: _PanicError This issue was debugged down to that dynamic_cast<_errorWrapper*> running inside libgolang.so on the following type defined in errors_test.so struct _MyWrapError final : _errorWrapper, object { returns NULL. This happened because in errors_test.so the type_info for _errorWrapper was emitted in addition to _errorWrapper's type_info emitted in libgolang with both of them being weak symbols: ((1.venv) ) poco:~/pygolang$ nm -DC golang/_errors_test.cpython-312.so |grep 'typeinfo for' 000000000004dba8 V typeinfo for _MyWrapError <-- NOTE 000000000004dac0 V typeinfo for _MyError 000000000004db10 V typeinfo for golang::_interface 000000000004dbe0 V typeinfo for golang::_errorWrapper 000000000004daf8 V typeinfo for golang::_error 000000000004db20 V typeinfo for golang::object U typeinfo for std::__ndk1::ios_base::failure U typeinfo for std::bad_typeid U typeinfo for std::range_error U typeinfo for std::domain_error U typeinfo for std::length_error U typeinfo for std::out_of_range U typeinfo for std::overflow_error U typeinfo for std::underflow_error U typeinfo for std::invalid_argument U typeinfo for std::bad_array_new_length U typeinfo for std::bad_cast U typeinfo for std::bad_alloc U typeinfo for std::exception ((1.venv) ) poco:~/pygolang$ nm -DC golang/runtime/liblibgolang.so |grep 'typeinfo for golang::' 00000000000905c8 V typeinfo for golang::PanicError 0000000000091128 V typeinfo for golang::_interface 0000000000091c28 V typeinfo for golang::_errorWrapper <-- NOTE 00000000000905e0 V typeinfo for golang::Bug 0000000000091cd0 V typeinfo for golang::fmt::_WrapError 0000000000091110 V typeinfo for golang::_error 0000000000091c68 V typeinfo for golang::errors::_TextError 0000000000091138 V typeinfo for golang::object 00000000000915e8 V typeinfo for golang::context::_CancelCtx 0000000000091518 V typeinfo for golang::context::_Background 0000000000091898 V typeinfo for golang::context::_TimeoutCtx 00000000000914a0 V typeinfo for golang::context::_BaseCtx 0000000000091488 V typeinfo for golang::context::_Context 0000000000091828 V typeinfo for golang::context::_ValueCtx 00000000000910d8 D typeinfo for golang::internal::syscall::_Errno 0000000000091920 V typeinfo for golang::context::_TimeoutCtx::_TimeoutCtx(double, double, golang::refptr<golang::context::_Context>)::{lambda()#1} 00000000000917c0 V typeinfo for golang::context::_BaseCtx::err()::{lambda()#1} and because of this typeid(_errorWrapper) returns different pointers in libgolang.so and errors_test.so . This did not matter with gcc and libstdc++ because, it seems, dynamic_cast is not checking only pointer equality for type_info, but on android/clang++ it made the difference. The problems happens not only for _errorWrapper, which did not have destructor, but also for _error and for _interface and _interface _did_ have destructor. But the destructor of _interface was not virtual. -> Fix this by making _interface destructor virtual and by making sure that ~_interface(), and destructors of all descendent classes, are defined in cpp part instead of being implicitly defined at header. We also need to make sure the same is true for default constructor, because else the compiler will generate default constructor itself in every translation unit, and this will lead to duplicate typeinfo and vtable to be still emitted. Note that making destructor virtual is not necessary for _interface semantic correctness - there we already have decref virtual, and when the decref is called by ~refptr, it is called on the real object type, which invokes real destructor even without that being virtual. But making the dtor virtual will not harm and will take only a few cycles which is ok for runtime-introspectible interface type. Note that we still have destructors non-virtual for regular types directly derived from object. After the fix: ((1.venv) ) poco:~/pygolang$ nm -DC golang/_errors_test.cpython-312.so |grep 'typeinfo for' 000000000004d768 V typeinfo for _MyWrapError 000000000004d6e8 V typeinfo for _MyError U typeinfo for golang::_errorWrapper U typeinfo for golang::_error 000000000004d720 V typeinfo for golang::object U typeinfo for std::__ndk1::ios_base::failure U typeinfo for std::bad_typeid U typeinfo for std::range_error U typeinfo for std::domain_error U typeinfo for std::length_error U typeinfo for std::out_of_range U typeinfo for std::overflow_error U typeinfo for std::underflow_error U typeinfo for std::invalid_argument U typeinfo for std::bad_array_new_length U typeinfo for std::bad_cast U typeinfo for std::bad_alloc U typeinfo for std::exception ((1.venv) ) poco:~/pygolang$ nm -DC golang/runtime/liblibgolang.so |grep 'typeinfo for golang::' 0000000000090b28 V typeinfo for golang::PanicError 0000000000090b58 D typeinfo for golang::_interface 0000000000090b80 D typeinfo for golang::_errorWrapper 0000000000090b40 V typeinfo for golang::Bug 00000000000922c0 V typeinfo for golang::fmt::_WrapError 0000000000090b68 D typeinfo for golang::_error 0000000000092250 V typeinfo for golang::errors::_TextError 0000000000091748 V typeinfo for golang::object 0000000000091dc8 V typeinfo for golang::context::_CancelCtx 0000000000091d30 V typeinfo for golang::context::_Background 0000000000092090 V typeinfo for golang::context::_TimeoutCtx 0000000000091cb0 V typeinfo for golang::context::_BaseCtx 0000000000091a50 D typeinfo for golang::context::_Context 0000000000092018 V typeinfo for golang::context::_ValueCtx 0000000000091710 D typeinfo for golang::internal::syscall::_Errno 0000000000092118 V typeinfo for golang::context::_TimeoutCtx::_TimeoutCtx(double, double, golang::refptr<golang::context::_Context>)::{lambda()#1} 0000000000091fa8 V typeinfo for golang::context::_BaseCtx::err()::{lambda()#1} i.e. typeinfo for _interface, _error and _errorWrapper turned to be D(defined) in libgolang.so and U(undefined) in _errors_test.so instead of previously being all V(weak) in both places. This fixes test_pyx_errors_unwrap_cpp and similar failures on android. Note that we still have typeinfo for object as V. This cannot be easily fixed without changing object to become polymorphic. It does not create problems in practice though. Note 2: types that are completely declared and defined in .cpp - e.g.. PanicError, are ok to come as V and not having extra care in the sources because by the construct they appear only in one translation unit and so the symbols are emitted only once. /reviewed-by @jerome /reviewed-on !386eca3654