diff --git a/Include/internal/pycore_abstract.h b/Include/internal/pycore_abstract.h index b9eb4fd9891e66..7006266815699d 100644 --- a/Include/internal/pycore_abstract.h +++ b/Include/internal/pycore_abstract.h @@ -13,7 +13,7 @@ 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 (tp_as_number->nb_index != NULL); } // Exported for external JIT support diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-05-04-10-35-49.gh-issue-149180.wCERVE.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-04-10-35-49.gh-issue-149180.wCERVE.rst new file mode 100644 index 00000000000000..947b844b3f9f7d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-05-04-10-35-49.gh-issue-149180.wCERVE.rst @@ -0,0 +1,5 @@ +Ensure :c:member:`~PyTypeObject.tp_as_number`, +:c:member:`~PyTypeObject.tp_as_sequence`, and +:c:member:`~PyTypeObject.tp_as_mapping` are never ``NULL`` after +:c:func:`PyType_Ready` by assigning shared empty structs as fallbacks. +Remove redundant ``NULL`` checks at callsites. 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/Objects/abstract.c b/Objects/abstract.c index 0bbf60840a3346..359c6ec26559a6 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -62,7 +62,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 +81,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 || + Py_TYPE(o)->tp_as_mapping->mp_length; } /* The length hint function returns a non-negative value from o.__len__() @@ -159,14 +159,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 +241,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 (_PyIndex_Check(key)) { Py_ssize_t key_value; key_value = PyNumber_AsSsize_t(key, PyExc_IndexError); @@ -275,13 +275,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 (_PyIndex_Check(key)) { Py_ssize_t key_value; key_value = PyNumber_AsSsize_t(key, PyExc_IndexError); @@ -915,7 +915,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 +943,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; @@ -1037,16 +1031,10 @@ ternary_op(PyObject *v, PyNumberMethods *mv = Py_TYPE(v)->tp_as_number; PyNumberMethods *mw = Py_TYPE(w)->tp_as_number; - ternaryfunc slotv; - if (mv != NULL) { - slotv = NB_TERNOP(mv, op_slot); - } - else { - slotv = NULL; - } + ternaryfunc 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 +1071,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 +1130,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 +1166,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 +1218,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 +1257,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 +1293,13 @@ 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,20 +1316,16 @@ 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) { - 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) { - /* Note that the right hand operand should not be - * mutated in this case so sq_inplace_repeat is not - * used. */ - if (mw->sq_repeat) - return sequence_repeat(mw->sq_repeat, w, v); - } + f = mv->sq_inplace_repeat; + if (f == NULL) + f = mv->sq_repeat; + if (f != NULL) + return sequence_repeat(f, v, w); + /* Note that the right hand operand should not be + * mutated in this case so sq_inplace_repeat is not + * used. */ + if (mw->sq_repeat) + return sequence_repeat(mw->sq_repeat, w, v); result = binop_type_error(v, w, "*="); } return result; @@ -1379,7 +1355,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 +1506,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 +1534,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 +1586,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 +1613,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 +1658,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 +1670,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 +1700,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 +1726,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 +1757,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 +1786,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 +1820,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 +1836,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 +1850,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 +1873,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 +1889,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 +1906,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 +1922,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 +1939,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 +1962,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 +2215,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 +2243,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 +2255,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/complexobject.c b/Objects/complexobject.c index 3612c2699a557d..dffe09169e0b5f 100644 --- a/Objects/complexobject.c +++ b/Objects/complexobject.c @@ -1149,8 +1149,8 @@ actual_complex_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) Py_complex c = ((PyComplexObject*)arg)->cval; res = complex_subtype_from_doubles(type, c.real, c.imag); } - else if ((nbr = Py_TYPE(arg)->tp_as_number) != NULL && - (nbr->nb_float != NULL || nbr->nb_index != NULL)) + else if ((nbr = Py_TYPE(arg)->tp_as_number)->nb_float != NULL || + nbr->nb_index != NULL) { /* The argument really is entirely real, and contributes nothing in the imaginary direction. @@ -1210,8 +1210,7 @@ complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i) } nbr = Py_TYPE(r)->tp_as_number; - if (nbr == NULL || - (nbr->nb_float == NULL && nbr->nb_index == NULL && !PyComplex_Check(r))) + if (nbr->nb_float == NULL && nbr->nb_index == NULL && !PyComplex_Check(r)) { PyErr_Format(PyExc_TypeError, "complex() argument 'real' must be a real number, not %T", @@ -1223,8 +1222,7 @@ complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i) } if (i != NULL) { nbi = Py_TYPE(i)->tp_as_number; - if (nbi == NULL || - (nbi->nb_float == NULL && nbi->nb_index == NULL && !PyComplex_Check(i))) + if (nbi->nb_float == NULL && nbi->nb_index == NULL && !PyComplex_Check(i)) { PyErr_Format(PyExc_TypeError, "complex() argument 'imag' must be a real number, not %T", @@ -1256,8 +1254,7 @@ complex_new_impl(PyTypeObject *type, PyObject *r, PyObject *i) Py_DECREF(r); } nbr = Py_TYPE(orig_r)->tp_as_number; - if (nbr == NULL || - (nbr->nb_float == NULL && nbr->nb_index == NULL)) + if (nbr->nb_float == NULL && nbr->nb_index == NULL) { if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, "complex() argument 'real' must be a real number, not %T", 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..3c69a60a930503 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -896,6 +896,10 @@ _PyType_CheckConsistency(PyTypeObject *type) CHECK(PyDict_Contains(lookup_tp_dict(type), &_Py_ID(__new__)) == 0); } + CHECK(type->tp_as_number != NULL); + CHECK(type->tp_as_sequence != NULL); + CHECK(type->tp_as_mapping != NULL); + return 1; #undef CHECK } @@ -8718,7 +8722,8 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base) if (type->tp_as_number != NULL && base->tp_as_number != NULL) { basebase = base->tp_base; - if (basebase->tp_as_number == NULL) + // basebase is NULL when base is 'object' (tp_base == NULL). + if (basebase == NULL || basebase->tp_as_number == NULL) basebase = NULL; COPYNUM(nb_add); COPYNUM(nb_subtract); @@ -8768,7 +8773,8 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base) if (type->tp_as_sequence != NULL && base->tp_as_sequence != NULL) { basebase = base->tp_base; - if (basebase->tp_as_sequence == NULL) + // basebase is NULL when base is 'object' (tp_base == NULL). + if (basebase == NULL || basebase->tp_as_sequence == NULL) basebase = NULL; COPYSEQ(sq_length); COPYSEQ(sq_concat); @@ -8782,7 +8788,8 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base) if (type->tp_as_mapping != NULL && base->tp_as_mapping != NULL) { basebase = base->tp_base; - if (basebase->tp_as_mapping == NULL) + // basebase is NULL when base is 'object' (tp_base == NULL). + if (basebase == NULL || basebase->tp_as_mapping == NULL) basebase = NULL; COPYMAP(mp_length); COPYMAP(mp_subscript); @@ -9155,6 +9162,10 @@ type_ready_mro(PyTypeObject *type, int initial) } +static const PyNumberMethods _Py_empty_number_methods = {0}; +static const PySequenceMethods _Py_empty_sequence_methods = {0}; +static const PyMappingMethods _Py_empty_mapping_methods = {0}; + // For static types, inherit tp_as_xxx structures from the base class // if it's NULL. // @@ -9216,6 +9227,19 @@ type_ready_inherit(PyTypeObject *type) type_ready_inherit_as_structs(type, base); } + // Ensure tp_as_number, tp_as_sequence, and tp_as_mapping are never + // NULL after PyType_Ready. Types that don't provide their own struct + // share a common empty one. + if (type->tp_as_number == NULL) { + type->tp_as_number = (PyNumberMethods *)&_Py_empty_number_methods; + } + if (type->tp_as_sequence == NULL) { + type->tp_as_sequence = (PySequenceMethods *)&_Py_empty_sequence_methods; + } + if (type->tp_as_mapping == NULL) { + type->tp_as_mapping = (PyMappingMethods *)&_Py_empty_mapping_methods; + } + /* Sanity check for tp_free. */ if (_PyType_IS_GC(type) && (type->tp_flags & Py_TPFLAGS_BASETYPE) && (type->tp_free == NULL || type->tp_free == PyObject_Free)) @@ -10447,10 +10471,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 +10649,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/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index aa89e312b62482..1b2cb84f9a81c4 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -352,6 +352,9 @@ Objects/typeobject.c - name_op - Objects/typeobject.c - slotdefs - # It initialized only once when main interpeter starts Objects/typeobject.c - slotdefs_dups - +Objects/typeobject.c - _Py_empty_mapping_methods - +Objects/typeobject.c - _Py_empty_number_methods - +Objects/typeobject.c - _Py_empty_sequence_methods - Objects/unicodeobject.c - stripfuncnames - Objects/unicodeobject.c - utf7_category - Objects/unicodeobject.c unicode_decode_call_errorhandler_wchar argparse -