@@ -992,6 +992,7 @@ static void
992992set_version_unlocked (PyTypeObject * tp , unsigned int version )
993993{
994994 ASSERT_TYPE_LOCK_HELD ();
995+ assert (version == 0 || (tp -> tp_versions_used != _Py_ATTR_CACHE_UNUSED ));
995996#ifndef Py_GIL_DISABLED
996997 PyInterpreterState * interp = _PyInterpreterState_GET ();
997998 // lookup the old version and set to null
@@ -1148,6 +1149,10 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
11481149 PyObject * b = PyTuple_GET_ITEM (bases , i );
11491150 PyTypeObject * cls = _PyType_CAST (b );
11501151
1152+ if (cls -> tp_versions_used >= _Py_ATTR_CACHE_UNUSED ) {
1153+ goto clear ;
1154+ }
1155+
11511156 if (!is_subtype_with_mro (lookup_tp_mro (type ), type , cls )) {
11521157 goto clear ;
11531158 }
@@ -1156,7 +1161,8 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
11561161
11571162 clear :
11581163 assert (!(type -> tp_flags & _Py_TPFLAGS_STATIC_BUILTIN ));
1159- set_version_unlocked (type , 0 ); /* 0 is not a valid version tag */
1164+ set_version_unlocked (type , 0 ); /* 0 is not a valid version tag */
1165+ type -> tp_versions_used = _Py_ATTR_CACHE_UNUSED ;
11601166 if (PyType_HasFeature (type , Py_TPFLAGS_HEAPTYPE )) {
11611167 // This field *must* be invalidated if the type is modified (see the
11621168 // comment on struct _specialization_cache):
@@ -1208,6 +1214,8 @@ _PyType_GetVersionForCurrentState(PyTypeObject *tp)
12081214
12091215
12101216#define MAX_VERSIONS_PER_CLASS 1000
1217+ _Static_assert (MAX_VERSIONS_PER_CLASS < _Py_ATTR_CACHE_UNUSED ,
1218+ "_Py_ATTR_CACHE_UNUSED must be bigger than max" );
12111219
12121220static int
12131221assign_version_tag (PyInterpreterState * interp , PyTypeObject * type )
@@ -1225,6 +1233,7 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
12251233 return 0 ;
12261234 }
12271235 if (type -> tp_versions_used >= MAX_VERSIONS_PER_CLASS ) {
1236+ /* (this includes `tp_versions_used == _Py_ATTR_CACHE_UNUSED`) */
12281237 return 0 ;
12291238 }
12301239
0 commit comments