diff --git a/test/test_hpytype.py b/test/test_hpytype.py index 6b01e1dcf..16459481a 100644 --- a/test/test_hpytype.py +++ b/test/test_hpytype.py @@ -248,6 +248,79 @@ class Sub(mod.Dummy): pass assert isinstance(Sub(), mod.Dummy) + def test_alignment(self): + """ + Test if the data pointer obtain by 'HPy_AsStruct' is aligned if the + basicsize of the base class is not. + Since we should not make assumptions about the alignment other than + it will be a power of 2, we will just work with even and odd numbers. + """ + import pytest + mod = self.make_module(""" + @DEFINE_PointObject + + static HPyType_Spec Dummy_spec = { + .name = "mytest.Dummy", + /* unaligned but large enough for PyObject_HEAD */ + .basicsize = 59, + .flags = HPy_TPFLAGS_DEFAULT | HPy_TPFLAGS_BASETYPE, + @IS_LEGACY + }; + + HPyDef_METH(get_data_ptr, "get_data_ptr", get_data_ptr_impl, HPyFunc_NOARGS) + static HPy get_data_ptr_impl(HPyContext *ctx, HPy self) + { + return HPyLong_FromUnsignedLongLong(ctx, + (unsigned long long) PointObject_AsStruct(ctx, self)); + } + + static HPyDef *DummySub_defines[] = { + &get_data_ptr, + NULL + }; + + static HPyType_Spec DummySub_spec = { + .name = "mytest.DummySub", + .basicsize = sizeof(PointObject), + .flags = HPy_TPFLAGS_DEFAULT, + @IS_LEGACY + .defines = DummySub_defines, + }; + + static void make_types(HPyContext *ctx, HPy module) + { + HPy h_Dummy = HPyType_FromSpec(ctx, &Dummy_spec, NULL); + if (HPy_IsNull(h_Dummy)) + return; + HPyType_SpecParam param[] = { + { HPyType_SpecParam_Base, h_Dummy }, + { (HPyType_SpecParam_Kind)0 } + }; + HPy h_DummySub = HPyType_FromSpec(ctx, &DummySub_spec, param); + if (HPy_IsNull(h_DummySub)) + return; + HPy_SetAttr_s(ctx, module, "Dummy", h_Dummy); + HPy_SetAttr_s(ctx, module, "DummySub", h_DummySub); + HPy_Close(ctx, h_Dummy); + HPy_Close(ctx, h_DummySub); + } + + @EXTRA_INIT_FUNC(make_types) + @INIT + """) + assert isinstance(mod.Dummy, type) + assert isinstance(mod.DummySub, type) + assert issubclass(mod.DummySub, mod.Dummy) + assert mod.Dummy.__basicsize__ >= 59 + if mod.Dummy.__basicsize__ % 2 != 1: + # be defensive: if, for some reason, the basicsize is even, then it + # could be properly aligned. In this case, we skip the test. + pytest.skip("basicsize is expected to be unaligned") + obj = mod.DummySub() + assert isinstance(obj, mod.Dummy) + assert isinstance(obj, mod.DummySub) + assert obj.get_data_ptr() % 2 == 0 + def test_doc_string(self): mod = self.make_module(""" static HPyType_Spec Dummy_spec = {