Skip to content

Commit dd4c8ac

Browse files
[3.12] gh-117482: Simplify the Fix For Builtin Types Slot Wrappers (gh-122241)
In gh-121602, I applied a fix to a builtin types initialization bug. That fix made sense in the context of some broader future changes, but introduced a little bit of extra complexity. For earlier versions those future changes are not relevant; we can avoid the extra complexity. Thus we can revert that earlier change and replace it with this one, which is more focused and conceptually simpler. This is essentially the implementation of an idea that @markshannon pointed out to me. Note that this change would be much smaller if we didn't have to deal with repr compatibility for builtin types that explicitly inherit tp slots (see expect_manually_inherited()). The alternative is to stop *explicitly* inheriting tp slots in static PyTypeObject values, which is churn that we can do separately. (cherry picked from commit 716c677, AKA gh-121932)
1 parent 1a104e5 commit dd4c8ac

File tree

2 files changed

+92
-35
lines changed

2 files changed

+92
-35
lines changed

Objects/typeobject.c

+92-30
Original file line numberDiff line numberDiff line change
@@ -117,17 +117,6 @@ static_builtin_index_clear(PyTypeObject *self)
117117
}
118118

119119

120-
/* In 3.13+ this is stored in _PyRuntimeState. */
121-
static PyTypeObject static_type_defs[_Py_MAX_STATIC_BUILTIN_TYPES];
122-
123-
static inline PyTypeObject *
124-
static_builtin_get_def(PyTypeObject *type)
125-
{
126-
size_t index = static_builtin_index_get(type);
127-
return &static_type_defs[index];
128-
}
129-
130-
131120
static inline static_builtin_state *
132121
static_builtin_state_get(PyInterpreterState *interp, PyTypeObject *self)
133122
{
@@ -6994,7 +6983,7 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
69946983
return 0;
69956984
}
69966985

6997-
static int add_operators(PyTypeObject *, PyTypeObject *);
6986+
static int add_operators(PyTypeObject *);
69986987
static int add_tp_new_wrapper(PyTypeObject *type);
69996988

70006989
#define COLLECTION_FLAGS (Py_TPFLAGS_SEQUENCE | Py_TPFLAGS_MAPPING)
@@ -7159,10 +7148,10 @@ type_dict_set_doc(PyTypeObject *type)
71597148

71607149

71617150
static int
7162-
type_ready_fill_dict(PyTypeObject *type, PyTypeObject *def)
7151+
type_ready_fill_dict(PyTypeObject *type)
71637152
{
71647153
/* Add type-specific descriptors to tp_dict */
7165-
if (add_operators(type, def) < 0) {
7154+
if (add_operators(type) < 0) {
71667155
return -1;
71677156
}
71687157
if (type_add_methods(type) < 0) {
@@ -7474,7 +7463,7 @@ type_ready_post_checks(PyTypeObject *type)
74747463

74757464

74767465
static int
7477-
type_ready(PyTypeObject *type, PyTypeObject *def, int rerunbuiltin)
7466+
type_ready(PyTypeObject *type, int rerunbuiltin)
74787467
{
74797468
_PyObject_ASSERT((PyObject *)type, !is_readying(type));
74807469
start_readying(type);
@@ -7511,7 +7500,7 @@ type_ready(PyTypeObject *type, PyTypeObject *def, int rerunbuiltin)
75117500
if (type_ready_set_new(type, rerunbuiltin) < 0) {
75127501
goto error;
75137502
}
7514-
if (type_ready_fill_dict(type, def) < 0) {
7503+
if (type_ready_fill_dict(type) < 0) {
75157504
goto error;
75167505
}
75177506
if (!rerunbuiltin) {
@@ -7563,7 +7552,7 @@ PyType_Ready(PyTypeObject *type)
75637552
type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
75647553
}
75657554

7566-
return type_ready(type, NULL, 0);
7555+
return type_ready(type, 0);
75677556
}
75687557

75697558
int
@@ -7593,12 +7582,7 @@ _PyStaticType_InitBuiltin(PyInterpreterState *interp, PyTypeObject *self)
75937582

75947583
static_builtin_state_init(interp, self);
75957584

7596-
PyTypeObject *def = static_builtin_get_def(self);
7597-
if (ismain) {
7598-
memcpy(def, self, sizeof(PyTypeObject));
7599-
}
7600-
7601-
int res = type_ready(self, def, !ismain);
7585+
int res = type_ready(self, !ismain);
76027586
if (res < 0) {
76037587
static_builtin_state_clear(interp, self);
76047588
}
@@ -10095,6 +10079,69 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
1009510079
return 0;
1009610080
}
1009710081

10082+
static int
10083+
expect_manually_inherited(PyTypeObject *type, void **slot)
10084+
{
10085+
PyObject *typeobj = (PyObject *)type;
10086+
if (slot == (void *)&type->tp_init) {
10087+
/* This is a best-effort list of builtin exception types
10088+
that have their own tp_init function. */
10089+
if (typeobj != PyExc_BaseException
10090+
&& typeobj != PyExc_BaseExceptionGroup
10091+
&& typeobj != PyExc_ImportError
10092+
&& typeobj != PyExc_NameError
10093+
&& typeobj != PyExc_OSError
10094+
&& typeobj != PyExc_StopIteration
10095+
&& typeobj != PyExc_SyntaxError
10096+
&& typeobj != PyExc_UnicodeDecodeError
10097+
&& typeobj != PyExc_UnicodeEncodeError)
10098+
{
10099+
return 1;
10100+
}
10101+
}
10102+
else if (slot == (void *)&type->tp_str) {
10103+
/* This is a best-effort list of builtin exception types
10104+
that have their own tp_str function. */
10105+
if (typeobj == PyExc_AttributeError || typeobj == PyExc_NameError) {
10106+
return 1;
10107+
}
10108+
}
10109+
else if (slot == (void *)&type->tp_getattr
10110+
|| slot == (void *)&type->tp_getattro)
10111+
{
10112+
/* This is a best-effort list of builtin types
10113+
that have their own tp_getattr function. */
10114+
if (typeobj == PyExc_BaseException
10115+
|| type == &PyBool_Type
10116+
|| type == &PyByteArray_Type
10117+
|| type == &PyBytes_Type
10118+
|| type == &PyClassMethod_Type
10119+
|| type == &PyComplex_Type
10120+
|| type == &PyDict_Type
10121+
|| type == &PyEnum_Type
10122+
|| type == &PyFilter_Type
10123+
|| type == &PyLong_Type
10124+
|| type == &PyList_Type
10125+
|| type == &PyMap_Type
10126+
|| type == &PyMemoryView_Type
10127+
|| type == &PyProperty_Type
10128+
|| type == &PyRange_Type
10129+
|| type == &PyReversed_Type
10130+
|| type == &PySet_Type
10131+
|| type == &PySlice_Type
10132+
|| type == &PyStaticMethod_Type
10133+
|| type == &PySuper_Type
10134+
|| type == &PyTuple_Type
10135+
|| type == &PyZip_Type)
10136+
{
10137+
return 1;
10138+
}
10139+
}
10140+
10141+
/* It must be inherited (see type_ready_inherit()).. */
10142+
return 0;
10143+
}
10144+
1009810145
/* This function is called by PyType_Ready() to populate the type's
1009910146
dictionary with method descriptors for function slots. For each
1010010147
function slot (like tp_repr) that's defined in the type, one or more
@@ -10126,24 +10173,39 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
1012610173
infinite recursion here.) */
1012710174

1012810175
static int
10129-
add_operators(PyTypeObject *type, PyTypeObject *def)
10176+
add_operators(PyTypeObject *type)
1013010177
{
1013110178
PyObject *dict = lookup_tp_dict(type);
1013210179
pytype_slotdef *p;
1013310180
PyObject *descr;
1013410181
void **ptr;
1013510182

10136-
assert(def == NULL || (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
10137-
if (def == NULL) {
10138-
def = type;
10139-
}
10140-
1014110183
for (p = slotdefs; p->name; p++) {
1014210184
if (p->wrapper == NULL)
1014310185
continue;
10144-
ptr = slotptr(def, p->offset);
10186+
ptr = slotptr(type, p->offset);
1014510187
if (!ptr || !*ptr)
1014610188
continue;
10189+
if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN
10190+
&& type->tp_base != NULL)
10191+
{
10192+
/* Also ignore when the type slot has been inherited. */
10193+
void **ptr_base = slotptr(type->tp_base, p->offset);
10194+
if (ptr_base && *ptr == *ptr_base) {
10195+
/* Ideally we would always ignore any manually inherited
10196+
slots, Which would mean inheriting the slot wrapper
10197+
using normal attribute lookup rather than keeping
10198+
a distinct copy. However, that would introduce
10199+
a slight change in behavior that could break
10200+
existing code.
10201+
10202+
In the meantime, look the other way when the definition
10203+
explicitly inherits the slot. */
10204+
if (!expect_manually_inherited(type, ptr)) {
10205+
continue;
10206+
}
10207+
}
10208+
}
1014710209
int r = PyDict_Contains(dict, p->name_strobj);
1014810210
if (r > 0)
1014910211
continue;

Tools/c-analyzer/cpython/globals-to-fix.tsv

-5
Original file line numberDiff line numberDiff line change
@@ -305,11 +305,6 @@ Objects/sliceobject.c - _Py_EllipsisObject -
305305
Python/instrumentation.c - _PyInstrumentation_DISABLE -
306306
Python/instrumentation.c - _PyInstrumentation_MISSING -
307307

308-
##-----------------------
309-
## other
310-
311-
Objects/typeobject.c - static_type_defs -
312-
313308

314309
##################################
315310
## global non-objects to fix in core code

0 commit comments

Comments
 (0)