diff options
| author | Victor Stinner <victor.stinner@gmail.com> | 2016-01-22 12:33:12 +0100 | 
|---|---|---|
| committer | Victor Stinner <victor.stinner@gmail.com> | 2016-01-22 12:33:12 +0100 | 
| commit | efb2413ce82acaa5dec43a8cb14aa7cdf2352fb1 (patch) | |
| tree | aa337dfb1c8b7c7911e150dd0ba94e762567fb48 /Objects | |
| parent | e3560a7dc9eeac324ff407588cb3f0b36ffe5c6e (diff) | |
| download | cpython-git-efb2413ce82acaa5dec43a8cb14aa7cdf2352fb1.tar.gz | |
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.
Diffstat (limited to 'Objects')
| -rw-r--r-- | Objects/codeobject.c | 139 | 
1 files changed, 138 insertions, 1 deletions
diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 0f03dfe4b3..e3d28e3f79 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -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);  | 
