@@ -1748,7 +1748,7 @@ type_get_mro(PyObject *tp, void *Py_UNUSED(closure))
1748
1748
static PyTypeObject * find_best_base (PyObject * );
1749
1749
static int mro_internal (PyTypeObject * , int , PyObject * * );
1750
1750
static int type_is_subtype_base_chain (PyTypeObject * , PyTypeObject * );
1751
- static int compatible_for_assignment (PyTypeObject * , PyTypeObject * , const char * );
1751
+ static int compatible_for_assignment (PyTypeObject * , PyTypeObject * , const char * , int );
1752
1752
static int add_subclass (PyTypeObject * , PyTypeObject * );
1753
1753
static int add_all_subclasses (PyTypeObject * type , PyObject * bases );
1754
1754
static void remove_subclass (PyTypeObject * , PyTypeObject * );
@@ -1886,7 +1886,7 @@ type_check_new_bases(PyTypeObject *type, PyObject *new_bases, PyTypeObject **bes
1886
1886
if (* best_base == NULL )
1887
1887
return -1 ;
1888
1888
1889
- if (!compatible_for_assignment (type -> tp_base , * best_base , "__bases__" )) {
1889
+ if (!compatible_for_assignment (type , * best_base , "__bases__" , 0 )) {
1890
1890
return -1 ;
1891
1891
}
1892
1892
@@ -7230,8 +7230,6 @@ compatible_with_tp_base(PyTypeObject *child)
7230
7230
child -> tp_itemsize == parent -> tp_itemsize &&
7231
7231
child -> tp_dictoffset == parent -> tp_dictoffset &&
7232
7232
child -> tp_weaklistoffset == parent -> tp_weaklistoffset &&
7233
- ((child -> tp_flags & Py_TPFLAGS_HAVE_GC ) ==
7234
- (parent -> tp_flags & Py_TPFLAGS_HAVE_GC )) &&
7235
7233
(child -> tp_dealloc == subtype_dealloc ||
7236
7234
child -> tp_dealloc == parent -> tp_dealloc ));
7237
7235
}
@@ -7243,34 +7241,47 @@ same_slots_added(PyTypeObject *a, PyTypeObject *b)
7243
7241
Py_ssize_t size ;
7244
7242
PyObject * slots_a , * slots_b ;
7245
7243
7246
- assert (base == b -> tp_base );
7244
+ // assert(base == b->tp_base);
7247
7245
size = base -> tp_basicsize ;
7248
7246
if (a -> tp_dictoffset == size && b -> tp_dictoffset == size )
7249
7247
size += sizeof (PyObject * );
7250
7248
if (a -> tp_weaklistoffset == size && b -> tp_weaklistoffset == size )
7251
7249
size += sizeof (PyObject * );
7252
7250
7253
7251
/* Check slots compliance */
7254
- if (!(a -> tp_flags & Py_TPFLAGS_HEAPTYPE ) ||
7255
- !(b -> tp_flags & Py_TPFLAGS_HEAPTYPE )) {
7256
- return 0 ;
7252
+ slots_a = (a -> tp_flags & Py_TPFLAGS_HEAPTYPE ) ? ((PyHeapTypeObject * )a )-> ht_slots : NULL ;
7253
+ slots_b = (b -> tp_flags & Py_TPFLAGS_HEAPTYPE ) ? ((PyHeapTypeObject * )b )-> ht_slots : NULL ;
7254
+ if (!slots_a ) {
7255
+ slots_a = (PyObject * )& _Py_SINGLETON (tuple_empty );
7257
7256
}
7258
- slots_a = ((PyHeapTypeObject * )a )-> ht_slots ;
7259
- slots_b = ((PyHeapTypeObject * )b )-> ht_slots ;
7260
- if (slots_a && slots_b ) {
7261
- if (PyObject_RichCompareBool (slots_a , slots_b , Py_EQ ) != 1 )
7262
- return 0 ;
7263
- size += sizeof (PyObject * ) * PyTuple_GET_SIZE (slots_a );
7257
+ if (!slots_b ) {
7258
+ slots_b = (PyObject * )& _Py_SINGLETON (tuple_empty );
7264
7259
}
7260
+ if (PyObject_RichCompareBool (slots_a , slots_b , Py_EQ ) != 1 )
7261
+ return 0 ;
7262
+ size += sizeof (PyObject * ) * PyTuple_GET_SIZE (slots_a );
7265
7263
return size == a -> tp_basicsize && size == b -> tp_basicsize ;
7266
7264
}
7267
7265
7268
7266
static int
7269
- compatible_for_assignment (PyTypeObject * oldto , PyTypeObject * newto , const char * attr )
7267
+ compatible_flags (int setclass , PyTypeObject * origto , PyTypeObject * newto , unsigned long flags )
7268
+ {
7269
+ /* For __class__ assignment, the flags should be the same.
7270
+ For __bases__ assignment, the new base flags can only be set
7271
+ if the original class flags are set.
7272
+ */
7273
+ return setclass ? (origto -> tp_flags & flags ) == (newto -> tp_flags & flags )
7274
+ : !(~(origto -> tp_flags & flags ) & (newto -> tp_flags & flags ));
7275
+ }
7276
+
7277
+ static int
7278
+ compatible_for_assignment (PyTypeObject * origto , PyTypeObject * newto ,
7279
+ const char * attr , int setclass )
7270
7280
{
7271
7281
PyTypeObject * newbase , * oldbase ;
7282
+ PyTypeObject * oldto = setclass ? origto : origto -> tp_base ;
7272
7283
7273
- if (newto -> tp_free != oldto -> tp_free ) {
7284
+ if (setclass && newto -> tp_free != oldto -> tp_free ) {
7274
7285
PyErr_Format (PyExc_TypeError ,
7275
7286
"%s assignment: "
7276
7287
"'%s' deallocator differs from '%s'" ,
@@ -7279,6 +7290,28 @@ compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, const char*
7279
7290
oldto -> tp_name );
7280
7291
return 0 ;
7281
7292
}
7293
+ if (!compatible_flags (setclass , origto , newto ,
7294
+ Py_TPFLAGS_HAVE_GC |
7295
+ Py_TPFLAGS_INLINE_VALUES |
7296
+ Py_TPFLAGS_PREHEADER ))
7297
+ {
7298
+ goto differs ;
7299
+ }
7300
+ /* For __class__ assignment, tp_dictoffset and tp_weaklistoffset should
7301
+ be the same for old and new types.
7302
+ For __bases__ assignment, they can only be set in the new base
7303
+ if they are set in the original class with the same value.
7304
+ */
7305
+ if ((setclass || newto -> tp_dictoffset )
7306
+ && origto -> tp_dictoffset != newto -> tp_dictoffset )
7307
+ {
7308
+ goto differs ;
7309
+ }
7310
+ if ((setclass || newto -> tp_weaklistoffset )
7311
+ && origto -> tp_weaklistoffset != newto -> tp_weaklistoffset )
7312
+ {
7313
+ goto differs ;
7314
+ }
7282
7315
/*
7283
7316
It's tricky to tell if two arbitrary types are sufficiently compatible as
7284
7317
to be interchangeable; e.g., even if they have the same tp_basicsize, they
@@ -7300,17 +7333,7 @@ compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, const char*
7300
7333
!same_slots_added (newbase , oldbase ))) {
7301
7334
goto differs ;
7302
7335
}
7303
- if ((oldto -> tp_flags & Py_TPFLAGS_INLINE_VALUES ) !=
7304
- ((newto -> tp_flags & Py_TPFLAGS_INLINE_VALUES )))
7305
- {
7306
- goto differs ;
7307
- }
7308
- /* The above does not check for the preheader */
7309
- if ((oldto -> tp_flags & Py_TPFLAGS_PREHEADER ) ==
7310
- ((newto -> tp_flags & Py_TPFLAGS_PREHEADER )))
7311
- {
7312
- return 1 ;
7313
- }
7336
+ return 1 ;
7314
7337
differs :
7315
7338
PyErr_Format (PyExc_TypeError ,
7316
7339
"%s assignment: "
@@ -7387,7 +7410,7 @@ object_set_class_world_stopped(PyObject *self, PyTypeObject *newto)
7387
7410
return -1 ;
7388
7411
}
7389
7412
7390
- if (compatible_for_assignment (oldto , newto , "__class__" )) {
7413
+ if (compatible_for_assignment (oldto , newto , "__class__" , 1 )) {
7391
7414
/* Changing the class will change the implicit dict keys,
7392
7415
* so we must materialize the dictionary first. */
7393
7416
if (oldto -> tp_flags & Py_TPFLAGS_INLINE_VALUES ) {
0 commit comments