From 8cefc49a253749263b1db4316d4017229da78c0d Mon Sep 17 00:00:00 2001 From: Neko Asakura Date: Wed, 6 May 2026 15:33:28 -0400 Subject: [PATCH] gh-149180: avoid double checking `tp_as_number`, `tp_as_sequence` and `tp_as_mapping` --- Include/internal/pycore_abstract.h | 3 +- Include/internal/pycore_typeobject.h | 23 +++ Lib/test/test_descr.py | 135 +++++++++++++++++ Modules/_bisectmodule.c | 4 +- Modules/_testinternalcapi/test_cases.c.h | 10 -- Objects/abstract.c | 175 ++++++++++------------- Objects/bytesobject.c | 2 +- Objects/floatobject.c | 4 +- Objects/object.c | 9 +- Objects/typeobject.c | 44 +++--- Python/bytecodes.c | 2 - Python/executor_cases.c.h | 52 ------- Python/generated_cases.c.h | 10 -- Python/optimizer_bytecodes.c | 6 +- Python/optimizer_cases.c.h | 6 +- Python/specialize.c | 22 ++- 16 files changed, 286 insertions(+), 221 deletions(-) diff --git a/Include/internal/pycore_abstract.h b/Include/internal/pycore_abstract.h index b9eb4fd9891e66..694f2d2aaca186 100644 --- a/Include/internal/pycore_abstract.h +++ b/Include/internal/pycore_abstract.h @@ -12,8 +12,7 @@ extern "C" { static inline int _PyIndex_Check(PyObject *obj) { - PyNumberMethods *tp_as_number = Py_TYPE(obj)->tp_as_number; - return (tp_as_number != NULL && tp_as_number->nb_index != NULL); + return Py_TYPE(obj)->tp_as_number->nb_index != NULL; } // Exported for external JIT support diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 8d48cf6605ca7e..34997a4c129af9 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -35,6 +35,29 @@ extern "C" { #define _Py_MAX_GLOBAL_TYPE_VERSION_TAG (_Py_TYPE_BASE_VERSION_TAG - 1) +extern const PyNumberMethods _PyType_EmptyNumberMethods; +extern const PySequenceMethods _PyType_EmptySequenceMethods; +extern const PyMappingMethods _PyType_EmptyMappingMethods; + +static inline int +_PyType_HasOwnNumberMethods(PyTypeObject *tp) +{ + return tp->tp_as_number != &_PyType_EmptyNumberMethods; +} + +static inline int +_PyType_HasOwnSequenceMethods(PyTypeObject *tp) +{ + return tp->tp_as_sequence != &_PyType_EmptySequenceMethods; +} + +static inline int +_PyType_HasOwnMappingMethods(PyTypeObject *tp) +{ + return tp->tp_as_mapping != &_PyType_EmptyMappingMethods; +} + + /* runtime lifecycle */ extern PyStatus _PyTypes_InitTypes(PyInterpreterState *); diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 8a8e70214e27ae..3c0f8cf2777437 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -16,6 +16,7 @@ from copy import deepcopy from contextlib import redirect_stdout from test import support +from test.support import import_helper from test.support.script_helper import assert_python_ok try: @@ -6319,5 +6320,139 @@ class IntSubclass(int): weakref_descriptor.__get__(IntSubclass(), IntSubclass) +class TestSubtableDispatchInvariants(unittest.TestCase): + + def test_int_times_list(self): + self.assertEqual(2 * [1], [1, 1]) + + def test_list_times_int(self): + self.assertEqual([1] * 2, [1, 1]) + + def test_int_times_tuple(self): + self.assertEqual(2 * (1,), (1, 1)) + + def test_tuple_times_int(self): + self.assertEqual((1,) * 2, (1, 1)) + + def test_int_times_str(self): + self.assertEqual(2 * "ab", "abab") + + def test_str_times_int(self): + self.assertEqual("ab" * 2, "abab") + + def test_int_times_bytes(self): + self.assertEqual(2 * b"ab", b"abab") + + def test_int_imul_list(self): + a = 2 + a *= [1] + self.assertEqual(a, [1, 1]) + + def test_list_imul_int(self): + a = [1] + a *= 2 + self.assertEqual(a, [1, 1]) + + def test_str_imul_int(self): + a = "ab" + a *= 2 + self.assertEqual(a, "abab") + + def test_class_without_mul_imul_seq_raises(self): + class NoSeq: + pass + a = NoSeq() + with self.assertRaises(TypeError): + a *= [1] + + def test_class_with_empty_seq_methods_imul_seq_raises(self): + class HasMulOnly: + def __mul__(self, other): + return NotImplemented + def __index__(self): + return 2 + a = HasMulOnly() + with self.assertRaises(TypeError): + a *= [1] + + def test_int_getitem_raises_type_error(self): + x = 5 + with self.assertRaises(TypeError): + x[0] + + def test_int_setitem_raises_type_error(self): + x = 5 + with self.assertRaises(TypeError): + x[0] = 1 + + def test_object_setitem_does_not_coerce_index(self): + class BadIndex: + called = False + + def __index__(self): + self.called = True + raise AssertionError("__index__ called") + + obj = object() + key = BadIndex() + with self.assertRaisesRegex(TypeError, + "does not support item assignment"): + obj[key] = 1 + self.assertFalse(key.called) + + def test_object_delitem_does_not_coerce_index(self): + class BadIndex: + called = False + + def __index__(self): + self.called = True + raise AssertionError("__index__ called") + + obj = object() + key = BadIndex() + with self.assertRaisesRegex(TypeError, + "does not support item deletion"): + del obj[key] + self.assertFalse(key.called) + + def test_object_bool_default(self): + class C: + pass + self.assertTrue(bool(C())) + + def test_object_bool_with_len(self): + class C: + def __len__(self): + return 0 + self.assertFalse(bool(C())) + + def test_object_bool_with_dunder_bool(self): + class C: + def __bool__(self): + return False + self.assertFalse(bool(C())) + + def test_sequence_check_int_is_false(self): + _testlimitedcapi = import_helper.import_module("_testlimitedcapi") + self.assertFalse(_testlimitedcapi.sequence_check(5)) + + def test_mapping_check_int_is_false(self): + _testlimitedcapi = import_helper.import_module("_testlimitedcapi") + self.assertFalse(_testlimitedcapi.mapping_check(5)) + + def test_int_index_protocol(self): + class MyInt(int): + pass + a = [10, 20, 30] + self.assertEqual(a[MyInt(1)], 20) + + def test_class_without_index_protocol_subscript_raises(self): + class NoIndex: + pass + a = [10, 20, 30] + with self.assertRaises(TypeError): + a[NoIndex()] + + if __name__ == "__main__": unittest.main() diff --git a/Modules/_bisectmodule.c b/Modules/_bisectmodule.c index 329aa8e117ec3c..9057323e5e0e5a 100644 --- a/Modules/_bisectmodule.c +++ b/Modules/_bisectmodule.c @@ -35,11 +35,11 @@ get_sq_item(PyObject *s) // The parts of PySequence_GetItem that we only need to do once PyTypeObject *tp = Py_TYPE(s); PySequenceMethods *m = tp->tp_as_sequence; - if (m && m->sq_item) { + if (m->sq_item) { return m->sq_item; } const char *msg; - if (tp->tp_as_mapping && tp->tp_as_mapping->mp_subscript) { + if (tp->tp_as_mapping->mp_subscript) { msg = "%.200s is not a sequence"; } else { diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index c65767e42da2e9..ec1ccddfcc07c0 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -649,11 +649,6 @@ { nos = stack_pointer[-2]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!Py_TYPE(o)->tp_as_mapping) { - UPDATE_MISS_STATS(BINARY_OP); - assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); - JUMP_TO_PREDICTED(BINARY_OP); - } if (Py_TYPE(o)->tp_as_mapping->mp_subscript != _PyDict_Subscript) { UPDATE_MISS_STATS(BINARY_OP); assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); @@ -12008,11 +12003,6 @@ { nos = stack_pointer[-2]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!Py_TYPE(o)->tp_as_mapping) { - UPDATE_MISS_STATS(STORE_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); - JUMP_TO_PREDICTED(STORE_SUBSCR); - } if (Py_TYPE(o)->tp_as_mapping->mp_ass_subscript != _PyDict_StoreSubscript) { UPDATE_MISS_STATS(STORE_SUBSCR); assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); diff --git a/Objects/abstract.c b/Objects/abstract.c index 0bbf60840a3346..9ceb53334c11a5 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -13,6 +13,7 @@ #include "pycore_pyerrors.h" // _PyErr_Occurred() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_tuple.h" // _PyTuple_FromArraySteal() +#include "pycore_typeobject.h" // _PyType_HasOwnSequenceMethods() #include "pycore_unionobject.h" // _PyUnion_Check() #include // offsetof() @@ -62,7 +63,7 @@ PyObject_Size(PyObject *o) } PySequenceMethods *m = Py_TYPE(o)->tp_as_sequence; - if (m && m->sq_length) { + if (m->sq_length) { Py_ssize_t len = m->sq_length(o); assert(_Py_CheckSlotResult(o, "__len__", len >= 0)); return len; @@ -81,8 +82,8 @@ PyObject_Length(PyObject *o) int _PyObject_HasLen(PyObject *o) { - return (Py_TYPE(o)->tp_as_sequence && Py_TYPE(o)->tp_as_sequence->sq_length) || - (Py_TYPE(o)->tp_as_mapping && Py_TYPE(o)->tp_as_mapping->mp_length); + return Py_TYPE(o)->tp_as_sequence->sq_length != NULL || + Py_TYPE(o)->tp_as_mapping->mp_length != NULL; } /* The length hint function returns a non-negative value from o.__len__() @@ -159,14 +160,14 @@ PyObject_GetItem(PyObject *o, PyObject *key) } PyMappingMethods *m = Py_TYPE(o)->tp_as_mapping; - if (m && m->mp_subscript) { + if (m->mp_subscript) { PyObject *item = m->mp_subscript(o, key); assert(_Py_CheckSlotResult(o, "__getitem__", item != NULL)); return item; } PySequenceMethods *ms = Py_TYPE(o)->tp_as_sequence; - if (ms && ms->sq_item) { + if (ms->sq_item) { if (_PyIndex_Check(key)) { Py_ssize_t key_value; key_value = PyNumber_AsSsize_t(key, PyExc_IndexError); @@ -241,13 +242,13 @@ PyObject_SetItem(PyObject *o, PyObject *key, PyObject *value) } PyMappingMethods *m = Py_TYPE(o)->tp_as_mapping; - if (m && m->mp_ass_subscript) { + if (m->mp_ass_subscript) { int res = m->mp_ass_subscript(o, key, value); assert(_Py_CheckSlotResult(o, "__setitem__", res >= 0)); return res; } - if (Py_TYPE(o)->tp_as_sequence) { + if (_PyType_HasOwnSequenceMethods(Py_TYPE(o))) { if (_PyIndex_Check(key)) { Py_ssize_t key_value; key_value = PyNumber_AsSsize_t(key, PyExc_IndexError); @@ -275,13 +276,13 @@ PyObject_DelItem(PyObject *o, PyObject *key) } PyMappingMethods *m = Py_TYPE(o)->tp_as_mapping; - if (m && m->mp_ass_subscript) { + if (m->mp_ass_subscript) { int res = m->mp_ass_subscript(o, key, (PyObject*)NULL); assert(_Py_CheckSlotResult(o, "__delitem__", res >= 0)); return res; } - if (Py_TYPE(o)->tp_as_sequence) { + if (_PyType_HasOwnSequenceMethods(Py_TYPE(o))) { if (_PyIndex_Check(key)) { Py_ssize_t key_value; key_value = PyNumber_AsSsize_t(key, PyExc_IndexError); @@ -915,7 +916,7 @@ PyNumber_Check(PyObject *o) if (o == NULL) return 0; PyNumberMethods *nb = Py_TYPE(o)->tp_as_number; - return nb && (nb->nb_index || nb->nb_int || nb->nb_float || PyComplex_Check(o)); + return nb->nb_index || nb->nb_int || nb->nb_float || PyComplex_Check(o); } /* Binary operators */ @@ -943,16 +944,10 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot #endif ) { - binaryfunc slotv; - if (Py_TYPE(v)->tp_as_number != NULL) { - slotv = NB_BINOP(Py_TYPE(v)->tp_as_number, op_slot); - } - else { - slotv = NULL; - } + binaryfunc slotv = NB_BINOP(Py_TYPE(v)->tp_as_number, op_slot); binaryfunc slotw; - if (!Py_IS_TYPE(w, Py_TYPE(v)) && Py_TYPE(w)->tp_as_number != NULL) { + if (!Py_IS_TYPE(w, Py_TYPE(v))) { slotw = NB_BINOP(Py_TYPE(w)->tp_as_number, op_slot); if (slotw == slotv) { slotw = NULL; @@ -1038,15 +1033,10 @@ ternary_op(PyObject *v, PyNumberMethods *mw = Py_TYPE(w)->tp_as_number; ternaryfunc slotv; - if (mv != NULL) { - slotv = NB_TERNOP(mv, op_slot); - } - else { - slotv = NULL; - } + slotv = NB_TERNOP(mv, op_slot); ternaryfunc slotw; - if (!Py_IS_TYPE(w, Py_TYPE(v)) && mw != NULL) { + if (!Py_IS_TYPE(w, Py_TYPE(v))) { slotw = NB_TERNOP(mw, op_slot); if (slotw == slotv) { slotw = NULL; @@ -1083,19 +1073,17 @@ ternary_op(PyObject *v, } PyNumberMethods *mz = Py_TYPE(z)->tp_as_number; - if (mz != NULL) { - ternaryfunc slotz = NB_TERNOP(mz, op_slot); - if (slotz == slotv || slotz == slotw) { - slotz = NULL; - } - if (slotz) { - PyObject *x = slotz(v, w, z); - assert(_Py_CheckSlotResult(z, op_name, x != NULL)); - if (x != Py_NotImplemented) { - return x; - } - Py_DECREF(x); /* can't do it */ + ternaryfunc slotz = NB_TERNOP(mz, op_slot); + if (slotz == slotv || slotz == slotw) { + slotz = NULL; + } + if (slotz) { + PyObject *x = slotz(v, w, z); + assert(_Py_CheckSlotResult(z, op_name, x != NULL)); + if (x != Py_NotImplemented) { + return x; } + Py_DECREF(x); /* can't do it */ } if (z == Py_None) { @@ -1144,7 +1132,7 @@ PyNumber_Add(PyObject *v, PyObject *w) Py_DECREF(result); PySequenceMethods *m = Py_TYPE(v)->tp_as_sequence; - if (m && m->sq_concat) { + if (m->sq_concat) { result = (*m->sq_concat)(v, w); assert(_Py_CheckSlotResult(v, "+", result != NULL)); return result; @@ -1180,10 +1168,10 @@ PyNumber_Multiply(PyObject *v, PyObject *w) PySequenceMethods *mv = Py_TYPE(v)->tp_as_sequence; PySequenceMethods *mw = Py_TYPE(w)->tp_as_sequence; Py_DECREF(result); - if (mv && mv->sq_repeat) { + if (mv->sq_repeat) { return sequence_repeat(mv->sq_repeat, v, w); } - else if (mw && mw->sq_repeat) { + else if (mw->sq_repeat) { return sequence_repeat(mw->sq_repeat, w, v); } result = binop_type_error(v, w, "*"); @@ -1232,16 +1220,14 @@ binary_iop1(PyObject *v, PyObject *w, const int iop_slot, const int op_slot ) { PyNumberMethods *mv = Py_TYPE(v)->tp_as_number; - if (mv != NULL) { - binaryfunc slot = NB_BINOP(mv, iop_slot); - if (slot) { - PyObject *x = (slot)(v, w); - assert(_Py_CheckSlotResult(v, op_name, x != NULL)); - if (x != Py_NotImplemented) { - return x; - } - Py_DECREF(x); + binaryfunc slot = NB_BINOP(mv, iop_slot); + if (slot) { + PyObject *x = (slot)(v, w); + assert(_Py_CheckSlotResult(v, op_name, x != NULL)); + if (x != Py_NotImplemented) { + return x; } + Py_DECREF(x); } #ifdef NDEBUG return binary_op1(v, w, op_slot); @@ -1273,15 +1259,13 @@ ternary_iop(PyObject *v, PyObject *w, PyObject *z, const int iop_slot, const int const char *op_name) { PyNumberMethods *mv = Py_TYPE(v)->tp_as_number; - if (mv != NULL) { - ternaryfunc slot = NB_TERNOP(mv, iop_slot); - if (slot) { - PyObject *x = (slot)(v, w, z); - if (x != Py_NotImplemented) { - return x; - } - Py_DECREF(x); + ternaryfunc slot = NB_TERNOP(mv, iop_slot); + if (slot) { + PyObject *x = (slot)(v, w, z); + if (x != Py_NotImplemented) { + return x; } + Py_DECREF(x); } return ternary_op(v, w, z, op_slot, op_name); } @@ -1311,15 +1295,14 @@ PyNumber_InPlaceAdd(PyObject *v, PyObject *w) if (result == Py_NotImplemented) { PySequenceMethods *m = Py_TYPE(v)->tp_as_sequence; Py_DECREF(result); - if (m != NULL) { - binaryfunc func = m->sq_inplace_concat; - if (func == NULL) - func = m->sq_concat; - if (func != NULL) { - result = func(v, w); - assert(_Py_CheckSlotResult(v, "+=", result != NULL)); - return result; - } + binaryfunc func = m->sq_inplace_concat; + if (func == NULL) { + func = m->sq_concat; + } + if (func != NULL) { + result = func(v, w); + assert(_Py_CheckSlotResult(v, "+=", result != NULL)); + return result; } result = binop_type_error(v, w, "+="); } @@ -1336,14 +1319,14 @@ PyNumber_InPlaceMultiply(PyObject *v, PyObject *w) PySequenceMethods *mv = Py_TYPE(v)->tp_as_sequence; PySequenceMethods *mw = Py_TYPE(w)->tp_as_sequence; Py_DECREF(result); - if (mv != NULL) { + if (_PyType_HasOwnSequenceMethods(Py_TYPE(v))) { f = mv->sq_inplace_repeat; if (f == NULL) f = mv->sq_repeat; if (f != NULL) return sequence_repeat(f, v, w); } - else if (mw != NULL) { + else if (_PyType_HasOwnSequenceMethods(Py_TYPE(w))) { /* Note that the right hand operand should not be * mutated in this case so sq_inplace_repeat is not * used. */ @@ -1379,7 +1362,7 @@ _PyNumber_InPlacePowerNoMod(PyObject *lhs, PyObject *rhs) } \ \ PyNumberMethods *m = Py_TYPE(o)->tp_as_number; \ - if (m && m->op) { \ + if (m->op) { \ PyObject *res = (*m->op)(o); \ assert(_Py_CheckSlotResult(o, #meth_name, res != NULL)); \ return res; \ @@ -1530,7 +1513,7 @@ PyNumber_Long(PyObject *o) return Py_NewRef(o); } m = Py_TYPE(o)->tp_as_number; - if (m && m->nb_int) { /* This should include subclasses of int */ + if (m->nb_int) { /* This should include subclasses of int */ /* Convert using the nb_int slot, which should return something of exact type int. */ result = m->nb_int(o); @@ -1558,7 +1541,7 @@ PyNumber_Long(PyObject *o) Py_SETREF(result, _PyLong_Copy((PyLongObject *)result)); return result; } - if (m && m->nb_index) { + if (m->nb_index) { return PyNumber_Index(o); } @@ -1610,7 +1593,7 @@ PyNumber_Float(PyObject *o) } PyNumberMethods *m = Py_TYPE(o)->tp_as_number; - if (m && m->nb_float) { /* This should include subclasses of float */ + if (m->nb_float) { /* This should include subclasses of float */ PyObject *res = m->nb_float(o); assert(_Py_CheckSlotResult(o, "__float__", res != NULL)); if (!res || PyFloat_CheckExact(res)) { @@ -1637,7 +1620,7 @@ PyNumber_Float(PyObject *o) return PyFloat_FromDouble(val); } - if (m && m->nb_index) { + if (m->nb_index) { PyObject *res = _PyNumber_Index(o); if (!res) { return NULL; @@ -1682,8 +1665,7 @@ PySequence_Check(PyObject *s) { if (PyDict_Check(s)) return 0; - return Py_TYPE(s)->tp_as_sequence && - Py_TYPE(s)->tp_as_sequence->sq_item != NULL; + return Py_TYPE(s)->tp_as_sequence->sq_item != NULL; } Py_ssize_t @@ -1695,13 +1677,13 @@ PySequence_Size(PyObject *s) } PySequenceMethods *m = Py_TYPE(s)->tp_as_sequence; - if (m && m->sq_length) { + if (m->sq_length) { Py_ssize_t len = m->sq_length(s); assert(_Py_CheckSlotResult(s, "__len__", len >= 0)); return len; } - if (Py_TYPE(s)->tp_as_mapping && Py_TYPE(s)->tp_as_mapping->mp_length) { + if (Py_TYPE(s)->tp_as_mapping->mp_length) { type_error("%.200s is not a sequence", s); return -1; } @@ -1725,7 +1707,7 @@ PySequence_Concat(PyObject *s, PyObject *o) } PySequenceMethods *m = Py_TYPE(s)->tp_as_sequence; - if (m && m->sq_concat) { + if (m->sq_concat) { PyObject *res = m->sq_concat(s, o); assert(_Py_CheckSlotResult(s, "+", res != NULL)); return res; @@ -1751,7 +1733,7 @@ PySequence_Repeat(PyObject *o, Py_ssize_t count) } PySequenceMethods *m = Py_TYPE(o)->tp_as_sequence; - if (m && m->sq_repeat) { + if (m->sq_repeat) { PyObject *res = m->sq_repeat(o, count); assert(_Py_CheckSlotResult(o, "*", res != NULL)); return res; @@ -1782,12 +1764,12 @@ PySequence_InPlaceConcat(PyObject *s, PyObject *o) } PySequenceMethods *m = Py_TYPE(s)->tp_as_sequence; - if (m && m->sq_inplace_concat) { + if (m->sq_inplace_concat) { PyObject *res = m->sq_inplace_concat(s, o); assert(_Py_CheckSlotResult(s, "+=", res != NULL)); return res; } - if (m && m->sq_concat) { + if (m->sq_concat) { PyObject *res = m->sq_concat(s, o); assert(_Py_CheckSlotResult(s, "+", res != NULL)); return res; @@ -1811,12 +1793,12 @@ PySequence_InPlaceRepeat(PyObject *o, Py_ssize_t count) } PySequenceMethods *m = Py_TYPE(o)->tp_as_sequence; - if (m && m->sq_inplace_repeat) { + if (m->sq_inplace_repeat) { PyObject *res = m->sq_inplace_repeat(o, count); assert(_Py_CheckSlotResult(o, "*=", res != NULL)); return res; } - if (m && m->sq_repeat) { + if (m->sq_repeat) { PyObject *res = m->sq_repeat(o, count); assert(_Py_CheckSlotResult(o, "*", res != NULL)); return res; @@ -1845,7 +1827,7 @@ PySequence_GetItem(PyObject *s, Py_ssize_t i) } PySequenceMethods *m = Py_TYPE(s)->tp_as_sequence; - if (m && m->sq_item) { + if (m->sq_item) { if (i < 0) { if (m->sq_length) { Py_ssize_t l = (*m->sq_length)(s); @@ -1861,7 +1843,7 @@ PySequence_GetItem(PyObject *s, Py_ssize_t i) return res; } - if (Py_TYPE(s)->tp_as_mapping && Py_TYPE(s)->tp_as_mapping->mp_subscript) { + if (Py_TYPE(s)->tp_as_mapping->mp_subscript) { return type_error("%.200s is not a sequence", s); } return type_error("'%.200s' object does not support indexing", s); @@ -1875,7 +1857,7 @@ PySequence_GetSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2) } PyMappingMethods *mp = Py_TYPE(s)->tp_as_mapping; - if (mp && mp->mp_subscript) { + if (mp->mp_subscript) { PyObject *slice = _PySlice_FromIndices(i1, i2); if (!slice) { return NULL; @@ -1898,7 +1880,7 @@ PySequence_SetItem(PyObject *s, Py_ssize_t i, PyObject *o) } PySequenceMethods *m = Py_TYPE(s)->tp_as_sequence; - if (m && m->sq_ass_item) { + if (m->sq_ass_item) { if (i < 0) { if (m->sq_length) { Py_ssize_t l = (*m->sq_length)(s); @@ -1914,7 +1896,7 @@ PySequence_SetItem(PyObject *s, Py_ssize_t i, PyObject *o) return res; } - if (Py_TYPE(s)->tp_as_mapping && Py_TYPE(s)->tp_as_mapping->mp_ass_subscript) { + if (Py_TYPE(s)->tp_as_mapping->mp_ass_subscript) { type_error("%.200s is not a sequence", s); return -1; } @@ -1931,7 +1913,7 @@ PySequence_DelItem(PyObject *s, Py_ssize_t i) } PySequenceMethods *m = Py_TYPE(s)->tp_as_sequence; - if (m && m->sq_ass_item) { + if (m->sq_ass_item) { if (i < 0) { if (m->sq_length) { Py_ssize_t l = (*m->sq_length)(s); @@ -1947,7 +1929,7 @@ PySequence_DelItem(PyObject *s, Py_ssize_t i) return res; } - if (Py_TYPE(s)->tp_as_mapping && Py_TYPE(s)->tp_as_mapping->mp_ass_subscript) { + if (Py_TYPE(s)->tp_as_mapping->mp_ass_subscript) { type_error("%.200s is not a sequence", s); return -1; } @@ -1964,7 +1946,7 @@ PySequence_SetSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2, PyObject *o) } PyMappingMethods *mp = Py_TYPE(s)->tp_as_mapping; - if (mp && mp->mp_ass_subscript) { + if (mp->mp_ass_subscript) { PyObject *slice = _PySlice_FromIndices(i1, i2); if (!slice) return -1; @@ -1987,7 +1969,7 @@ PySequence_DelSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2) } PyMappingMethods *mp = Py_TYPE(s)->tp_as_mapping; - if (mp && mp->mp_ass_subscript) { + if (mp->mp_ass_subscript) { PyObject *slice = _PySlice_FromIndices(i1, i2); if (!slice) { return -1; @@ -2240,7 +2222,7 @@ int PySequence_Contains(PyObject *seq, PyObject *ob) { PySequenceMethods *sqm = Py_TYPE(seq)->tp_as_sequence; - if (sqm != NULL && sqm->sq_contains != NULL) { + if (sqm->sq_contains != NULL) { int res = (*sqm->sq_contains)(seq, ob); assert(_Py_CheckSlotResult(seq, "__contains__", res >= 0)); return res; @@ -2268,8 +2250,7 @@ PySequence_Index(PyObject *s, PyObject *o) int PyMapping_Check(PyObject *o) { - return o && Py_TYPE(o)->tp_as_mapping && - Py_TYPE(o)->tp_as_mapping->mp_subscript; + return o && Py_TYPE(o)->tp_as_mapping->mp_subscript; } Py_ssize_t @@ -2281,13 +2262,13 @@ PyMapping_Size(PyObject *o) } PyMappingMethods *m = Py_TYPE(o)->tp_as_mapping; - if (m && m->mp_length) { + if (m->mp_length) { Py_ssize_t len = m->mp_length(o); assert(_Py_CheckSlotResult(o, "__len__", len >= 0)); return len; } - if (Py_TYPE(o)->tp_as_sequence && Py_TYPE(o)->tp_as_sequence->sq_length) { + if (Py_TYPE(o)->tp_as_sequence->sq_length) { type_error("%.200s is not a mapping", o); return -1; } diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c index 8a9d1b133affb3..ede5e94367b286 100644 --- a/Objects/bytesobject.c +++ b/Objects/bytesobject.c @@ -661,7 +661,7 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len, arglen = -1; argidx = -2; } - if (Py_TYPE(args)->tp_as_mapping && Py_TYPE(args)->tp_as_mapping->mp_subscript && + if (Py_TYPE(args)->tp_as_mapping->mp_subscript && !PyTuple_Check(args) && !PyBytes_Check(args) && !PyUnicode_Check(args) && !PyByteArray_Check(args)) { dict = args; diff --git a/Objects/floatobject.c b/Objects/floatobject.c index d91468dddded9b..f70473296dd7dd 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -260,8 +260,8 @@ PyFloat_AsDouble(PyObject *op) } nb = Py_TYPE(op)->tp_as_number; - if (nb == NULL || nb->nb_float == NULL) { - if (nb && nb->nb_index) { + if (nb->nb_float == NULL) { + if (nb->nb_index) { PyObject *res = _PyNumber_Index(op); if (!res) { return -1; diff --git a/Objects/object.c b/Objects/object.c index e0e26bb50d3653..a9d80df1f27a5d 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2144,14 +2144,11 @@ PyObject_IsTrue(PyObject *v) return 0; if (v == Py_None) return 0; - else if (Py_TYPE(v)->tp_as_number != NULL && - Py_TYPE(v)->tp_as_number->nb_bool != NULL) + else if (Py_TYPE(v)->tp_as_number->nb_bool != NULL) res = (*Py_TYPE(v)->tp_as_number->nb_bool)(v); - else if (Py_TYPE(v)->tp_as_mapping != NULL && - Py_TYPE(v)->tp_as_mapping->mp_length != NULL) + else if (Py_TYPE(v)->tp_as_mapping->mp_length != NULL) res = (*Py_TYPE(v)->tp_as_mapping->mp_length)(v); - else if (Py_TYPE(v)->tp_as_sequence != NULL && - Py_TYPE(v)->tp_as_sequence->sq_length != NULL) + else if (Py_TYPE(v)->tp_as_sequence->sq_length != NULL) res = (*Py_TYPE(v)->tp_as_sequence->sq_length)(v); else return 1; diff --git a/Objects/typeobject.c b/Objects/typeobject.c index fb3c7101410683..5662bda42c3990 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -881,6 +881,9 @@ _PyType_CheckConsistency(PyTypeObject *type) CHECK(Py_REFCNT(type) >= 1); CHECK(PyType_Check(type)); + CHECK(type->tp_as_number != NULL); + CHECK(type->tp_as_sequence != NULL); + CHECK(type->tp_as_mapping != NULL); CHECK(!is_readying(type)); CHECK(lookup_tp_dict(type) != NULL); @@ -8716,10 +8719,8 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base) /* This won't inherit indirect slots (from tp_as_number etc.) if type doesn't provide the space. */ - if (type->tp_as_number != NULL && base->tp_as_number != NULL) { + if (type->tp_as_number != NULL) { basebase = base->tp_base; - if (basebase->tp_as_number == NULL) - basebase = NULL; COPYNUM(nb_add); COPYNUM(nb_subtract); COPYNUM(nb_multiply); @@ -8766,10 +8767,8 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base) COPYASYNC(am_anext); } - if (type->tp_as_sequence != NULL && base->tp_as_sequence != NULL) { + if (type->tp_as_sequence != NULL) { basebase = base->tp_base; - if (basebase->tp_as_sequence == NULL) - basebase = NULL; COPYSEQ(sq_length); COPYSEQ(sq_concat); COPYSEQ(sq_repeat); @@ -8780,10 +8779,8 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base) COPYSEQ(sq_inplace_repeat); } - if (type->tp_as_mapping != NULL && base->tp_as_mapping != NULL) { + if (type->tp_as_mapping != NULL) { basebase = base->tp_base; - if (basebase->tp_as_mapping == NULL) - basebase = NULL; COPYMAP(mp_length); COPYMAP(mp_subscript); COPYMAP(mp_ass_subscript); @@ -9155,6 +9152,24 @@ type_ready_mro(PyTypeObject *type, int initial) } +const PyNumberMethods _PyType_EmptyNumberMethods = {0}; +const PySequenceMethods _PyType_EmptySequenceMethods = {0}; +const PyMappingMethods _PyType_EmptyMappingMethods = {0}; + +static void +type_ready_install_method_tables(PyTypeObject *type) +{ + if (type->tp_as_number == NULL) { + type->tp_as_number = (PyNumberMethods *)&_PyType_EmptyNumberMethods; + } + if (type->tp_as_sequence == NULL) { + type->tp_as_sequence = (PySequenceMethods *)&_PyType_EmptySequenceMethods; + } + if (type->tp_as_mapping == NULL) { + type->tp_as_mapping = (PyMappingMethods *)&_PyType_EmptyMappingMethods; + } +} + // For static types, inherit tp_as_xxx structures from the base class // if it's NULL. // @@ -9215,6 +9230,7 @@ type_ready_inherit(PyTypeObject *type) if (base != NULL) { type_ready_inherit_as_structs(type, base); } + type_ready_install_method_tables(type); /* Sanity check for tp_free. */ if (_PyType_IS_GC(type) && (type->tp_flags & Py_TPFLAGS_BASETYPE) && @@ -9865,7 +9881,7 @@ getindex(PyObject *self, PyObject *arg) return -1; if (i < 0) { PySequenceMethods *sq = Py_TYPE(self)->tp_as_sequence; - if (sq && sq->sq_length) { + if (sq->sq_length) { Py_ssize_t n = (*sq->sq_length)(self); if (n < 0) { assert(PyErr_Occurred()); @@ -10447,10 +10463,8 @@ FUNCNAME(PyObject *self, PyObject *other) \ PyObject* stack[2]; \ PyThreadState *tstate = _PyThreadState_GET(); \ int do_other = !Py_IS_TYPE(self, Py_TYPE(other)) && \ - Py_TYPE(other)->tp_as_number != NULL && \ Py_TYPE(other)->tp_as_number->SLOTNAME == TESTFUNC; \ - if (Py_TYPE(self)->tp_as_number != NULL && \ - Py_TYPE(self)->tp_as_number->SLOTNAME == TESTFUNC) { \ + if (Py_TYPE(self)->tp_as_number->SLOTNAME == TESTFUNC) { \ PyObject *r; \ if (do_other && PyType_IsSubtype(Py_TYPE(other), Py_TYPE(self))) { \ int ok = method_is_overloaded(self, other, &_Py_ID(RDUNDER)); \ @@ -10627,10 +10641,8 @@ slot_nb_power(PyObject *self, PyObject *other, PyObject *modulus) PyObject* stack[3]; PyThreadState *tstate = _PyThreadState_GET(); int do_other = !Py_IS_TYPE(self, Py_TYPE(other)) && - Py_TYPE(other)->tp_as_number != NULL && Py_TYPE(other)->tp_as_number->nb_power == slot_nb_power; - if (Py_TYPE(self)->tp_as_number != NULL && - Py_TYPE(self)->tp_as_number->nb_power == slot_nb_power) { + if (Py_TYPE(self)->tp_as_number->nb_power == slot_nb_power) { PyObject *r; if (do_other && PyType_IsSubtype(Py_TYPE(other), Py_TYPE(self))) { int ok = method_is_overloaded(self, other, &_Py_ID(__rpow__)); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index daa989eb32ca1f..be97a49f251128 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1279,13 +1279,11 @@ dummy_func( op(_GUARD_NOS_DICT_SUBSCRIPT, (nos, unused -- nos, unused)) { PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - DEOPT_IF(!Py_TYPE(o)->tp_as_mapping); DEOPT_IF(Py_TYPE(o)->tp_as_mapping->mp_subscript != _PyDict_Subscript); } op(_GUARD_NOS_DICT_STORE_SUBSCRIPT, (unused, nos, unused -- unused, nos, unused)) { PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - DEOPT_IF(!Py_TYPE(o)->tp_as_mapping); DEOPT_IF(Py_TYPE(o)->tp_as_mapping->mp_ass_subscript != _PyDict_StoreSubscript); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 29c901b2bae723..69d6ccb0989116 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -7466,11 +7466,6 @@ _PyStackRef nos; nos = stack_pointer[-2]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!Py_TYPE(o)->tp_as_mapping) { - UOP_STAT_INC(uopcode, miss); - SET_CURRENT_CACHED_VALUES(0); - JUMP_TO_JUMP_TARGET(); - } if (Py_TYPE(o)->tp_as_mapping->mp_subscript != _PyDict_Subscript) { UOP_STAT_INC(uopcode, miss); SET_CURRENT_CACHED_VALUES(0); @@ -7492,12 +7487,6 @@ _PyStackRef _stack_item_0 = _tos_cache0; nos = stack_pointer[-1]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!Py_TYPE(o)->tp_as_mapping) { - UOP_STAT_INC(uopcode, miss); - _tos_cache0 = _stack_item_0; - SET_CURRENT_CACHED_VALUES(1); - JUMP_TO_JUMP_TARGET(); - } if (Py_TYPE(o)->tp_as_mapping->mp_subscript != _PyDict_Subscript) { UOP_STAT_INC(uopcode, miss); _tos_cache0 = _stack_item_0; @@ -7521,13 +7510,6 @@ _PyStackRef _stack_item_1 = _tos_cache1; nos = _stack_item_0; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!Py_TYPE(o)->tp_as_mapping) { - UOP_STAT_INC(uopcode, miss); - _tos_cache1 = _stack_item_1; - _tos_cache0 = nos; - SET_CURRENT_CACHED_VALUES(2); - JUMP_TO_JUMP_TARGET(); - } if (Py_TYPE(o)->tp_as_mapping->mp_subscript != _PyDict_Subscript) { UOP_STAT_INC(uopcode, miss); _tos_cache1 = _stack_item_1; @@ -7551,14 +7533,6 @@ _PyStackRef _stack_item_2 = _tos_cache2; nos = _stack_item_1; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!Py_TYPE(o)->tp_as_mapping) { - UOP_STAT_INC(uopcode, miss); - _tos_cache2 = _stack_item_2; - _tos_cache1 = nos; - _tos_cache0 = _stack_item_0; - SET_CURRENT_CACHED_VALUES(3); - JUMP_TO_JUMP_TARGET(); - } if (Py_TYPE(o)->tp_as_mapping->mp_subscript != _PyDict_Subscript) { UOP_STAT_INC(uopcode, miss); _tos_cache2 = _stack_item_2; @@ -7581,11 +7555,6 @@ _PyStackRef nos; nos = stack_pointer[-2]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!Py_TYPE(o)->tp_as_mapping) { - UOP_STAT_INC(uopcode, miss); - SET_CURRENT_CACHED_VALUES(0); - JUMP_TO_JUMP_TARGET(); - } if (Py_TYPE(o)->tp_as_mapping->mp_ass_subscript != _PyDict_StoreSubscript) { UOP_STAT_INC(uopcode, miss); SET_CURRENT_CACHED_VALUES(0); @@ -7608,12 +7577,6 @@ _PyStackRef _stack_item_0 = _tos_cache0; nos = stack_pointer[-1]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!Py_TYPE(o)->tp_as_mapping) { - UOP_STAT_INC(uopcode, miss); - _tos_cache0 = _stack_item_0; - SET_CURRENT_CACHED_VALUES(1); - JUMP_TO_JUMP_TARGET(); - } if (Py_TYPE(o)->tp_as_mapping->mp_ass_subscript != _PyDict_StoreSubscript) { UOP_STAT_INC(uopcode, miss); _tos_cache0 = _stack_item_0; @@ -7638,13 +7601,6 @@ _PyStackRef _stack_item_1 = _tos_cache1; nos = _stack_item_0; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!Py_TYPE(o)->tp_as_mapping) { - UOP_STAT_INC(uopcode, miss); - _tos_cache1 = _stack_item_1; - _tos_cache0 = nos; - SET_CURRENT_CACHED_VALUES(2); - JUMP_TO_JUMP_TARGET(); - } if (Py_TYPE(o)->tp_as_mapping->mp_ass_subscript != _PyDict_StoreSubscript) { UOP_STAT_INC(uopcode, miss); _tos_cache1 = _stack_item_1; @@ -7671,14 +7627,6 @@ _PyStackRef _stack_item_2 = _tos_cache2; nos = _stack_item_1; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!Py_TYPE(o)->tp_as_mapping) { - UOP_STAT_INC(uopcode, miss); - _tos_cache2 = _stack_item_2; - _tos_cache1 = nos; - _tos_cache0 = _stack_item_0; - SET_CURRENT_CACHED_VALUES(3); - JUMP_TO_JUMP_TARGET(); - } if (Py_TYPE(o)->tp_as_mapping->mp_ass_subscript != _PyDict_StoreSubscript) { UOP_STAT_INC(uopcode, miss); _tos_cache2 = _stack_item_2; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 7fea3ddfc6f559..f1f004489f765d 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -649,11 +649,6 @@ { nos = stack_pointer[-2]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!Py_TYPE(o)->tp_as_mapping) { - UPDATE_MISS_STATS(BINARY_OP); - assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); - JUMP_TO_PREDICTED(BINARY_OP); - } if (Py_TYPE(o)->tp_as_mapping->mp_subscript != _PyDict_Subscript) { UPDATE_MISS_STATS(BINARY_OP); assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); @@ -12005,11 +12000,6 @@ { nos = stack_pointer[-2]; PyObject *o = PyStackRef_AsPyObjectBorrow(nos); - if (!Py_TYPE(o)->tp_as_mapping) { - UPDATE_MISS_STATS(STORE_SUBSCR); - assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); - JUMP_TO_PREDICTED(STORE_SUBSCR); - } if (Py_TYPE(o)->tp_as_mapping->mp_ass_subscript != _PyDict_StoreSubscript) { UPDATE_MISS_STATS(STORE_SUBSCR); assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR)); diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index f10c304fa02001..89dde24dfcbea1 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -2195,8 +2195,7 @@ dummy_func(void) { tp = sym_get_probable_type(nos); definite = false; } - if (tp && tp->tp_as_mapping && - tp->tp_as_mapping->mp_subscript == _PyDict_Subscript) { + if (tp && tp->tp_as_mapping->mp_subscript == _PyDict_Subscript) { if (definite) { ADD_OP(_NOP, 0, 0); } @@ -2216,8 +2215,7 @@ dummy_func(void) { tp = sym_get_probable_type(nos); definite = false; } - if (tp && tp->tp_as_mapping && - tp->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript) { + if (tp && tp->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript) { if (definite) { ADD_OP(_NOP, 0, 0); } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 0cad3524850df4..bf999652e3f3f8 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -1484,8 +1484,7 @@ tp = sym_get_probable_type(nos); definite = false; } - if (tp && tp->tp_as_mapping && - tp->tp_as_mapping->mp_subscript == _PyDict_Subscript) { + if (tp && tp->tp_as_mapping->mp_subscript == _PyDict_Subscript) { if (definite) { ADD_OP(_NOP, 0, 0); } @@ -1508,8 +1507,7 @@ tp = sym_get_probable_type(nos); definite = false; } - if (tp && tp->tp_as_mapping && - tp->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript) { + if (tp && tp->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript) { if (definite) { ADD_OP(_NOP, 0, 0); } diff --git a/Python/specialize.c b/Python/specialize.c index b50728d4a2f3f3..47b7459ce42af4 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1503,8 +1503,9 @@ store_subscr_fail_kind(PyObject *container, PyObject *sub) { PyTypeObject *container_type = Py_TYPE(container); PyMappingMethods *as_mapping = container_type->tp_as_mapping; - if (as_mapping && (as_mapping->mp_ass_subscript - == PyDict_Type.tp_as_mapping->mp_ass_subscript)) { + if (as_mapping->mp_ass_subscript + == PyDict_Type.tp_as_mapping->mp_ass_subscript) + { return SPEC_FAIL_SUBSCR_DICT_SUBCLASS_NO_OVERRIDE; } if (PyObject_CheckBuffer(container)) { @@ -1595,8 +1596,7 @@ _Py_Specialize_StoreSubscr(_PyStackRef container_st, _PyStackRef sub_st, _Py_COD return; } } - if (container_type->tp_as_mapping != NULL && - container_type->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript) + if (container_type->tp_as_mapping->mp_ass_subscript == _PyDict_StoreSubscript) { specialize(instr, STORE_SUBSCR_DICT); return; @@ -2431,8 +2431,7 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in } } } - if (Py_TYPE(lhs)->tp_as_mapping != NULL && - Py_TYPE(lhs)->tp_as_mapping->mp_subscript == _PyDict_Subscript) + if (Py_TYPE(lhs)->tp_as_mapping->mp_subscript == _PyDict_Subscript) { specialize(instr, BINARY_OP_SUBSCR_DICT); return; @@ -2821,17 +2820,14 @@ to_bool_fail_kind(PyObject *value) static int check_type_always_true(PyTypeObject *ty) { - PyNumberMethods *nb = ty->tp_as_number; - if (nb && nb->nb_bool) { + if (ty->tp_as_number->nb_bool) { return SPEC_FAIL_TO_BOOL_NUMBER; } - PyMappingMethods *mp = ty->tp_as_mapping; - if (mp && mp->mp_length) { + if (ty->tp_as_mapping->mp_length) { return SPEC_FAIL_TO_BOOL_MAPPING; } - PySequenceMethods *sq = ty->tp_as_sequence; - if (sq && sq->sq_length) { - return SPEC_FAIL_TO_BOOL_SEQUENCE; + if (ty->tp_as_sequence->sq_length) { + return SPEC_FAIL_TO_BOOL_SEQUENCE; } return 0; }