@@ -284,8 +284,9 @@ def emit_line() -> None:
284
284
285
285
fields ["tp_members" ] = members_name
286
286
fields ["tp_basicsize" ] = f"{ base_size } + 2*sizeof(PyObject *)"
287
- fields ["tp_dictoffset" ] = base_size
288
- fields ["tp_weaklistoffset" ] = weak_offset
287
+ if emitter .capi_version < (3 , 12 ):
288
+ fields ["tp_dictoffset" ] = base_size
289
+ fields ["tp_weaklistoffset" ] = weak_offset
289
290
else :
290
291
fields ["tp_basicsize" ] = base_size
291
292
@@ -341,6 +342,8 @@ def emit_line() -> None:
341
342
# This is just a placeholder to please CPython. It will be
342
343
# overridden during setup.
343
344
fields ["tp_call" ] = "PyVectorcall_Call"
345
+ if has_managed_dict (cl , emitter ):
346
+ flags .append ("Py_TPFLAGS_MANAGED_DICT" )
344
347
fields ["tp_flags" ] = " | " .join (flags )
345
348
346
349
emitter .emit_line (f"static PyTypeObject { emitter .type_struct_name (cl )} _template_ = {{" )
@@ -730,7 +733,9 @@ def generate_traverse_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -
730
733
for base in reversed (cl .base_mro ):
731
734
for attr , rtype in base .attributes .items ():
732
735
emitter .emit_gc_visit (f"self->{ emitter .attr (attr )} " , rtype )
733
- if cl .has_dict :
736
+ if has_managed_dict (cl , emitter ):
737
+ emitter .emit_line ("_PyObject_VisitManagedDict((PyObject *)self, visit, arg);" )
738
+ elif cl .has_dict :
734
739
struct_name = cl .struct_name (emitter .names )
735
740
# __dict__ lives right after the struct and __weakref__ lives right after that
736
741
emitter .emit_gc_visit (
@@ -751,7 +756,9 @@ def generate_clear_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -> N
751
756
for base in reversed (cl .base_mro ):
752
757
for attr , rtype in base .attributes .items ():
753
758
emitter .emit_gc_clear (f"self->{ emitter .attr (attr )} " , rtype )
754
- if cl .has_dict :
759
+ if has_managed_dict (cl , emitter ):
760
+ emitter .emit_line ("_PyObject_ClearManagedDict((PyObject *)self);" )
761
+ elif cl .has_dict :
755
762
struct_name = cl .struct_name (emitter .names )
756
763
# __dict__ lives right after the struct and __weakref__ lives right after that
757
764
emitter .emit_gc_clear (
@@ -1040,3 +1047,15 @@ def generate_property_setter(
1040
1047
)
1041
1048
emitter .emit_line ("return 0;" )
1042
1049
emitter .emit_line ("}" )
1050
+
1051
+
1052
+ def has_managed_dict (cl : ClassIR , emitter : Emitter ) -> bool :
1053
+ """Should the class get the Py_TPFLAGS_MANAGED_DICT flag?"""
1054
+ # On 3.11 and earlier the flag doesn't exist and we use
1055
+ # tp_dictoffset instead. If a class inherits from Exception, the
1056
+ # flag conflicts with tp_dictoffset set in the base class.
1057
+ return (
1058
+ emitter .capi_version >= (3 , 12 )
1059
+ and cl .has_dict
1060
+ and cl .builtin_base != "PyBaseExceptionObject"
1061
+ )
0 commit comments