@@ -967,6 +967,7 @@ static void
967967set_version_unlocked (PyTypeObject * tp , unsigned int version )
968968{
969969 ASSERT_TYPE_LOCK_HELD ();
970+ assert (version == 0 || (tp -> tp_versions_used != _Py_ATTR_CACHE_UNUSED ));
970971#ifndef Py_GIL_DISABLED
971972 if (version ) {
972973 tp -> tp_versions_used ++ ;
@@ -1109,6 +1110,10 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
11091110 PyObject * b = PyTuple_GET_ITEM (bases , i );
11101111 PyTypeObject * cls = _PyType_CAST (b );
11111112
1113+ if (cls -> tp_versions_used >= _Py_ATTR_CACHE_UNUSED ) {
1114+ goto clear ;
1115+ }
1116+
11121117 if (!is_subtype_with_mro (lookup_tp_mro (type ), type , cls )) {
11131118 goto clear ;
11141119 }
@@ -1117,7 +1122,8 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
11171122
11181123 clear :
11191124 assert (!(type -> tp_flags & _Py_TPFLAGS_STATIC_BUILTIN ));
1120- set_version_unlocked (type , 0 ); /* 0 is not a valid version tag */
1125+ set_version_unlocked (type , 0 ); /* 0 is not a valid version tag */
1126+ type -> tp_versions_used = _Py_ATTR_CACHE_UNUSED ;
11211127 if (PyType_HasFeature (type , Py_TPFLAGS_HEAPTYPE )) {
11221128 // This field *must* be invalidated if the type is modified (see the
11231129 // comment on struct _specialization_cache):
@@ -1127,6 +1133,9 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
11271133}
11281134
11291135#define MAX_VERSIONS_PER_CLASS 1000
1136+ #if _Py_ATTR_CACHE_UNUSED < MAX_VERSIONS_PER_CLASS
1137+ #error "_Py_ATTR_CACHE_UNUSED must be bigger than max"
1138+ #endif
11301139
11311140static int
11321141assign_version_tag (PyInterpreterState * interp , PyTypeObject * type )
@@ -1144,6 +1153,7 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
11441153 return 0 ;
11451154 }
11461155 if (type -> tp_versions_used >= MAX_VERSIONS_PER_CLASS ) {
1156+ /* (this includes `tp_versions_used == _Py_ATTR_CACHE_UNUSED`) */
11471157 return 0 ;
11481158 }
11491159
0 commit comments