mirror of
https://github.com/python/cpython.git
synced 2025-08-30 05:35:08 +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
d52513cb22
commit
3cdd5fb970
5 changed files with 245 additions and 49 deletions
|
@ -572,6 +572,88 @@ if 1:
|
|||
exec(memoryview(b"ax = 123")[1:-1], namespace)
|
||||
self.assertEqual(namespace['x'], 12)
|
||||
|
||||
def check_constant(self, func, expected):
|
||||
for const in func.__code__.co_consts:
|
||||
if repr(const) == repr(expected):
|
||||
break
|
||||
else:
|
||||
self.fail("unable to find constant %r in %r"
|
||||
% (expected, func.__code__.co_consts))
|
||||
|
||||
# Merging equal constants is not a strict requirement for the Python
|
||||
# semantics, it's a more an implementation detail.
|
||||
@support.cpython_only
|
||||
def test_merge_constants(self):
|
||||
# Issue #25843: compile() must merge constants which are equal
|
||||
# and have the same type.
|
||||
|
||||
def check_same_constant(const):
|
||||
ns = {}
|
||||
code = "f1, f2 = lambda: %r, lambda: %r" % (const, const)
|
||||
exec(code, ns)
|
||||
f1 = ns['f1']
|
||||
f2 = ns['f2']
|
||||
self.assertIs(f1.__code__, f2.__code__)
|
||||
self.check_constant(f1, const)
|
||||
self.assertEqual(repr(f1()), repr(const))
|
||||
|
||||
check_same_constant(None)
|
||||
check_same_constant(0)
|
||||
check_same_constant(0.0)
|
||||
check_same_constant(b'abc')
|
||||
check_same_constant('abc')
|
||||
|
||||
# Note: "lambda: ..." emits "LOAD_CONST Ellipsis",
|
||||
# whereas "lambda: Ellipsis" emits "LOAD_GLOBAL Ellipsis"
|
||||
f1, f2 = lambda: ..., lambda: ...
|
||||
self.assertIs(f1.__code__, f2.__code__)
|
||||
self.check_constant(f1, Ellipsis)
|
||||
self.assertEqual(repr(f1()), repr(Ellipsis))
|
||||
|
||||
# {0} is converted to a constant frozenset({0}) by the peephole
|
||||
# optimizer
|
||||
f1, f2 = lambda x: x in {0}, lambda x: x in {0}
|
||||
self.assertIs(f1.__code__, f2.__code__)
|
||||
self.check_constant(f1, frozenset({0}))
|
||||
self.assertTrue(f1(0))
|
||||
|
||||
def test_dont_merge_constants(self):
|
||||
# Issue #25843: compile() must not merge constants which are equal
|
||||
# but have a different type.
|
||||
|
||||
def check_different_constants(const1, const2):
|
||||
ns = {}
|
||||
exec("f1, f2 = lambda: %r, lambda: %r" % (const1, const2), ns)
|
||||
f1 = ns['f1']
|
||||
f2 = ns['f2']
|
||||
self.assertIsNot(f1.__code__, f2.__code__)
|
||||
self.check_constant(f1, const1)
|
||||
self.check_constant(f2, const2)
|
||||
self.assertEqual(repr(f1()), repr(const1))
|
||||
self.assertEqual(repr(f2()), repr(const2))
|
||||
|
||||
check_different_constants(0, 0.0)
|
||||
check_different_constants(+0.0, -0.0)
|
||||
check_different_constants((0,), (0.0,))
|
||||
|
||||
# check_different_constants() cannot be used because repr(-0j) is
|
||||
# '(-0-0j)', but when '(-0-0j)' is evaluated to 0j: we loose the sign.
|
||||
f1, f2 = lambda: +0.0j, lambda: -0.0j
|
||||
self.assertIsNot(f1.__code__, f2.__code__)
|
||||
self.check_constant(f1, +0.0j)
|
||||
self.check_constant(f2, -0.0j)
|
||||
self.assertEqual(repr(f1()), repr(+0.0j))
|
||||
self.assertEqual(repr(f2()), repr(-0.0j))
|
||||
|
||||
# {0} is converted to a constant frozenset({0}) by the peephole
|
||||
# optimizer
|
||||
f1, f2 = lambda x: x in {0}, lambda x: x in {0.0}
|
||||
self.assertIsNot(f1.__code__, f2.__code__)
|
||||
self.check_constant(f1, frozenset({0}))
|
||||
self.check_constant(f2, frozenset({0.0}))
|
||||
self.assertTrue(f1(0))
|
||||
self.assertTrue(f2(0.0))
|
||||
|
||||
|
||||
class TestStackSize(unittest.TestCase):
|
||||
# These tests check that the computed stack size for a code object
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue