mirror of
https://github.com/python/cpython.git
synced 2025-07-09 20:35:26 +00:00
code_richcompare() now uses the constants types
Issue #25843: When compiling code, don't merge constants if they are equal but have a different types. For example, "f1, f2 = lambda: 1, lambda: 1.0" is now correctly compiled to two different functions: f1() returns 1 (int) and f2() returns 1.0 (int), even if 1 and 1.0 are equal. Add a new _PyCode_ConstantKey() private function.
This commit is contained in:
parent
e3560a7dc9
commit
efb2413ce8
5 changed files with 245 additions and 49 deletions
|
@ -409,11 +409,135 @@ code_repr(PyCodeObject *co)
|
|||
}
|
||||
}
|
||||
|
||||
PyObject*
|
||||
_PyCode_ConstantKey(PyObject *op)
|
||||
{
|
||||
PyObject *key;
|
||||
|
||||
/* Py_None and Py_Ellipsis are singleton */
|
||||
if (op == Py_None || op == Py_Ellipsis
|
||||
|| PyLong_CheckExact(op)
|
||||
|| PyBool_Check(op)
|
||||
|| PyBytes_CheckExact(op)
|
||||
|| PyUnicode_CheckExact(op)
|
||||
/* code_richcompare() uses _PyCode_ConstantKey() internally */
|
||||
|| PyCode_Check(op)) {
|
||||
key = PyTuple_Pack(2, Py_TYPE(op), op);
|
||||
}
|
||||
else if (PyFloat_CheckExact(op)) {
|
||||
double d = PyFloat_AS_DOUBLE(op);
|
||||
/* all we need is to make the tuple different in either the 0.0
|
||||
* or -0.0 case from all others, just to avoid the "coercion".
|
||||
*/
|
||||
if (d == 0.0 && copysign(1.0, d) < 0.0)
|
||||
key = PyTuple_Pack(3, Py_TYPE(op), op, Py_None);
|
||||
else
|
||||
key = PyTuple_Pack(2, Py_TYPE(op), op);
|
||||
}
|
||||
else if (PyComplex_CheckExact(op)) {
|
||||
Py_complex z;
|
||||
int real_negzero, imag_negzero;
|
||||
/* For the complex case we must make complex(x, 0.)
|
||||
different from complex(x, -0.) and complex(0., y)
|
||||
different from complex(-0., y), for any x and y.
|
||||
All four complex zeros must be distinguished.*/
|
||||
z = PyComplex_AsCComplex(op);
|
||||
real_negzero = z.real == 0.0 && copysign(1.0, z.real) < 0.0;
|
||||
imag_negzero = z.imag == 0.0 && copysign(1.0, z.imag) < 0.0;
|
||||
/* use True, False and None singleton as tags for the real and imag
|
||||
* sign, to make tuples different */
|
||||
if (real_negzero && imag_negzero) {
|
||||
key = PyTuple_Pack(3, Py_TYPE(op), op, Py_True);
|
||||
}
|
||||
else if (imag_negzero) {
|
||||
key = PyTuple_Pack(3, Py_TYPE(op), op, Py_False);
|
||||
}
|
||||
else if (real_negzero) {
|
||||
key = PyTuple_Pack(3, Py_TYPE(op), op, Py_None);
|
||||
}
|
||||
else {
|
||||
key = PyTuple_Pack(2, Py_TYPE(op), op);
|
||||
}
|
||||
}
|
||||
else if (PyTuple_CheckExact(op)) {
|
||||
Py_ssize_t i, len;
|
||||
PyObject *tuple;
|
||||
|
||||
len = PyTuple_GET_SIZE(op);
|
||||
tuple = PyTuple_New(len);
|
||||
if (tuple == NULL)
|
||||
return NULL;
|
||||
|
||||
for (i=0; i < len; i++) {
|
||||
PyObject *item, *item_key;
|
||||
|
||||
item = PyTuple_GET_ITEM(op, i);
|
||||
item_key = _PyCode_ConstantKey(item);
|
||||
if (item_key == NULL) {
|
||||
Py_DECREF(tuple);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyTuple_SET_ITEM(tuple, i, item_key);
|
||||
}
|
||||
|
||||
key = PyTuple_Pack(3, Py_TYPE(op), op, tuple);
|
||||
Py_DECREF(tuple);
|
||||
}
|
||||
else if (PyFrozenSet_CheckExact(op)) {
|
||||
Py_ssize_t pos = 0;
|
||||
PyObject *item;
|
||||
Py_hash_t hash;
|
||||
Py_ssize_t i, len;
|
||||
PyObject *tuple, *set;
|
||||
|
||||
len = PySet_GET_SIZE(op);
|
||||
tuple = PyTuple_New(len);
|
||||
if (tuple == NULL)
|
||||
return NULL;
|
||||
|
||||
i = 0;
|
||||
while (_PySet_NextEntry(op, &pos, &item, &hash)) {
|
||||
PyObject *item_key;
|
||||
|
||||
item_key = _PyCode_ConstantKey(item);
|
||||
if (item_key == NULL) {
|
||||
Py_DECREF(tuple);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(i < len);
|
||||
PyTuple_SET_ITEM(tuple, i, item_key);
|
||||
i++;
|
||||
}
|
||||
set = PyFrozenSet_New(tuple);
|
||||
Py_DECREF(tuple);
|
||||
if (set == NULL)
|
||||
return NULL;
|
||||
|
||||
key = PyTuple_Pack(3, Py_TYPE(op), op, set);
|
||||
Py_DECREF(set);
|
||||
return key;
|
||||
}
|
||||
else {
|
||||
/* for other types, use the object identifier as an unique identifier
|
||||
* to ensure that they are seen as unequal. */
|
||||
PyObject *obj_id = PyLong_FromVoidPtr(op);
|
||||
if (obj_id == NULL)
|
||||
return NULL;
|
||||
|
||||
key = PyTuple_Pack(3, Py_TYPE(op), op, obj_id);
|
||||
Py_DECREF(obj_id);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
code_richcompare(PyObject *self, PyObject *other, int op)
|
||||
{
|
||||
PyCodeObject *co, *cp;
|
||||
int eq;
|
||||
PyObject *consts1, *consts2;
|
||||
PyObject *res;
|
||||
|
||||
if ((op != Py_EQ && op != Py_NE) ||
|
||||
|
@ -439,8 +563,21 @@ code_richcompare(PyObject *self, PyObject *other, int op)
|
|||
if (!eq) goto unequal;
|
||||
eq = PyObject_RichCompareBool(co->co_code, cp->co_code, Py_EQ);
|
||||
if (eq <= 0) goto unequal;
|
||||
eq = PyObject_RichCompareBool(co->co_consts, cp->co_consts, Py_EQ);
|
||||
|
||||
/* compare constants */
|
||||
consts1 = _PyCode_ConstantKey(co->co_consts);
|
||||
if (!consts1)
|
||||
return NULL;
|
||||
consts2 = _PyCode_ConstantKey(cp->co_consts);
|
||||
if (!consts2) {
|
||||
Py_DECREF(consts1);
|
||||
return NULL;
|
||||
}
|
||||
eq = PyObject_RichCompareBool(consts1, consts2, Py_EQ);
|
||||
Py_DECREF(consts1);
|
||||
Py_DECREF(consts2);
|
||||
if (eq <= 0) goto unequal;
|
||||
|
||||
eq = PyObject_RichCompareBool(co->co_names, cp->co_names, Py_EQ);
|
||||
if (eq <= 0) goto unequal;
|
||||
eq = PyObject_RichCompareBool(co->co_varnames, cp->co_varnames, Py_EQ);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue