From 946ab7bfb8aa883d48ec4b376208110e0a2c9b38 Mon Sep 17 00:00:00 2001 From: Aaron Jomy Date: Fri, 28 Aug 2026 17:55:34 +0200 Subject: [PATCH] [cpyrt] Cache anonymous-enum constant values in CPPDataMember dm_get computed the value of an anonymous-enum constant but did not cache it. kIsEnumPrep is cleared on the first access and kIsEnumType was never set, so later accesses skipped the enum branch and read the instance memory at the member's offset instead. Cache the value in fDescription and set kIsEnumType, as the named-enum path does. --- src/cpyrt/CPPDataMember.cxx | 14 +++++++++++--- test/test_datatypes.py | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/cpyrt/CPPDataMember.cxx b/src/cpyrt/CPPDataMember.cxx index c2543dc..b7a5787 100644 --- a/src/cpyrt/CPPDataMember.cxx +++ b/src/cpyrt/CPPDataMember.cxx @@ -88,9 +88,17 @@ static PyObject* dm_get(CPPDataMember* dm, CPPInstance* pyobj, } if (interop::IsEnumConstant(dm->fScope)) { - // anonymous enum - return pyval_from_enum(interop::ResolveEnum(dm->fScope), nullptr, nullptr, - dm->fScope); + // anonymous enum; cache the value in fDescription like the named case + // above: once kIsEnumPrep is cleared this block is not reached again + PyObject* pyval = pyval_from_enum(interop::ResolveEnum(dm->fScope), + nullptr, nullptr, dm->fScope); + if (pyval) { + Py_DECREF(dm->fDescription); + dm->fDescription = pyval; + dm->fFlags |= kIsEnumType; + Py_INCREF(pyval); + return pyval; + } } } // non-initialized or public data accesses through class (e.g. by help()) diff --git a/test/test_datatypes.py b/test/test_datatypes.py index e5f5f56..b172c2e 100644 --- a/test/test_datatypes.py +++ b/test/test_datatypes.py @@ -2736,3 +2736,21 @@ def test55_qt_cache_alias_collision(self): ns.take_schar("e") ns.take_int8(101) raises(TypeError, ns.take_int8, "e") + + +class TestANONENUM: + def test01_anonymous_enum_repeated_access(self): + """An anonymous-enum constant keeps its value across accesses""" + + import cppjit + + cppjit.cppdef("""\ + namespace AnonEnum { struct Holder { enum { kAnon = 42 }; }; }""") + + h = cppjit.gbl.AnonEnum.Holder() + + # the value is computed on the first access and cached; without the + # cache later accesses fall through to the data-member converter + assert h.kAnon == 42 + assert h.kAnon == 42 + assert h.kAnon == 42