Skip to content

Commit 76500f9

Browse files
jgarzikclaude
andcommitted
Fix None==0 identity comparison bug; import test_compare (15 tests, 0 skips)
Bug fix: - COMPARE_OP identity fallback: check both payload AND tag for equality. None (payload=0, TAG_NONE) was comparing equal to SmallInt 0 (payload=0, TAG_SMALLINT) because only payload was checked. New test: - test_compare.py: int, float, string, list, tuple, set, dict, None, bool comparisons plus custom __eq__/__ne__/__lt__/__gt__ (15 tests) Total CPython test suites: 26 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 261d891 commit 76500f9

3 files changed

Lines changed: 149 additions & 0 deletions

File tree

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ gen-cpython-tests:
106106
@$(PYTHON) -m py_compile tests/cpython/test_raise.py
107107
@echo "Compiling tests/cpython/test_class.py..."
108108
@$(PYTHON) -m py_compile tests/cpython/test_class.py
109+
@echo "Compiling tests/cpython/test_compare.py..."
110+
@$(PYTHON) -m py_compile tests/cpython/test_compare.py
109111
@echo "Done."
110112

111113
check-cpython: $(TARGET) gen-cpython-tests
@@ -159,3 +161,5 @@ check-cpython: $(TARGET) gen-cpython-tests
159161
@./apython tests/cpython/__pycache__/test_raise.cpython-312.pyc
160162
@echo "Running CPython test_class.py..."
161163
@./apython tests/cpython/__pycache__/test_class.cpython-312.pyc
164+
@echo "Running CPython test_compare.py..."
165+
@./apython tests/cpython/__pycache__/test_compare.cpython-312.pyc

src/opcodes_misc.asm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,12 @@ section .text
10131013
mov rsi, [rsp + BO_RIGHT]
10141014
mov rdi, [rsp + BO_LEFT]
10151015
cmp rdi, rsi
1016+
jne .cmp_id_not_equal
1017+
; Payloads match — also check tags (None payload=0 vs SmallInt 0)
1018+
mov rdi, [rsp + BO_LTAG]
1019+
cmp rdi, [rsp + BO_RTAG]
10161020
je .cmp_id_equal
1021+
.cmp_id_not_equal:
10171022
; Not equal
10181023
cmp ecx, PY_NE
10191024
je .cmp_id_true

tests/cpython/test_compare.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"""Tests for comparison operators — adapted from CPython test_compare.py"""
2+
3+
import unittest
4+
5+
6+
class ComparisonSimpleTest(unittest.TestCase):
7+
8+
def test_int_comparisons(self):
9+
self.assertTrue(1 < 2)
10+
self.assertTrue(2 > 1)
11+
self.assertTrue(1 <= 1)
12+
self.assertTrue(1 >= 1)
13+
self.assertTrue(1 == 1)
14+
self.assertTrue(1 != 2)
15+
self.assertFalse(1 > 2)
16+
self.assertFalse(1 == 2)
17+
self.assertFalse(1 != 1)
18+
19+
def test_float_comparisons(self):
20+
self.assertTrue(1.0 < 2.0)
21+
self.assertTrue(2.0 > 1.0)
22+
self.assertTrue(1.0 == 1.0)
23+
self.assertTrue(1.0 != 2.0)
24+
25+
def test_mixed_int_float(self):
26+
self.assertTrue(1 == 1.0)
27+
self.assertTrue(1 < 1.5)
28+
self.assertTrue(2.0 > 1)
29+
self.assertTrue(1 != 1.5)
30+
31+
def test_string_comparisons(self):
32+
self.assertTrue('a' < 'b')
33+
self.assertTrue('b' > 'a')
34+
self.assertTrue('abc' == 'abc')
35+
self.assertTrue('abc' != 'abd')
36+
self.assertTrue('abc' < 'abd')
37+
self.assertTrue('abc' < 'abcd')
38+
39+
def test_list_comparisons(self):
40+
self.assertTrue([1, 2] == [1, 2])
41+
self.assertTrue([1, 2] != [1, 3])
42+
self.assertTrue([1, 2] < [1, 3])
43+
self.assertTrue([1, 2] < [1, 2, 3])
44+
self.assertTrue([1, 3] > [1, 2])
45+
46+
def test_tuple_comparisons(self):
47+
self.assertTrue((1, 2) == (1, 2))
48+
self.assertTrue((1, 2) != (1, 3))
49+
self.assertTrue((1, 2) < (1, 3))
50+
self.assertTrue((1, 2) < (1, 2, 3))
51+
52+
def test_none_comparisons(self):
53+
self.assertTrue(None == None)
54+
self.assertFalse(None != None)
55+
self.assertTrue(None is None)
56+
self.assertFalse(None is not None)
57+
self.assertFalse(None == 0)
58+
self.assertFalse(None == "")
59+
self.assertFalse(None == [])
60+
61+
def test_bool_comparisons(self):
62+
self.assertTrue(True == True)
63+
self.assertTrue(False == False)
64+
self.assertTrue(True != False)
65+
self.assertTrue(True == 1)
66+
self.assertTrue(False == 0)
67+
self.assertTrue(True > False)
68+
69+
def test_identity(self):
70+
a = [1, 2]
71+
b = a
72+
c = [1, 2]
73+
self.assertTrue(a is b)
74+
self.assertFalse(a is c)
75+
self.assertTrue(a is not c)
76+
self.assertEqual(a, c)
77+
78+
def test_chained_comparisons(self):
79+
self.assertTrue(1 < 2 < 3)
80+
self.assertFalse(1 < 2 > 3)
81+
self.assertTrue(1 <= 1 < 2)
82+
self.assertTrue(1 == 1 <= 2)
83+
84+
def test_ne_defaults_to_not_eq(self):
85+
class Cmp:
86+
def __init__(self, arg):
87+
self.arg = arg
88+
def __eq__(self, other):
89+
return self.arg == other
90+
91+
a = Cmp(1)
92+
self.assertTrue(a == 1)
93+
self.assertFalse(a == 2)
94+
95+
def test_custom_eq(self):
96+
class C:
97+
def __init__(self, val):
98+
self.val = val
99+
def __eq__(self, other):
100+
if isinstance(other, C):
101+
return self.val == other.val
102+
return self.val == other
103+
def __ne__(self, other):
104+
if isinstance(other, C):
105+
return self.val != other.val
106+
return self.val != other
107+
self.assertTrue(C(1) == C(1))
108+
self.assertFalse(C(1) == C(2))
109+
self.assertTrue(C(1) != C(2))
110+
self.assertTrue(C(42) == 42)
111+
112+
def test_custom_ordering(self):
113+
class C:
114+
def __init__(self, val):
115+
self.val = val
116+
def __lt__(self, other):
117+
return self.val < other.val
118+
def __le__(self, other):
119+
return self.val <= other.val
120+
def __gt__(self, other):
121+
return self.val > other.val
122+
def __ge__(self, other):
123+
return self.val >= other.val
124+
self.assertTrue(C(1) < C(2))
125+
self.assertTrue(C(2) > C(1))
126+
self.assertTrue(C(1) <= C(1))
127+
self.assertTrue(C(1) >= C(1))
128+
129+
def test_set_equality(self):
130+
self.assertTrue({1, 2, 3} == {3, 2, 1})
131+
self.assertFalse({1, 2} == {1, 3})
132+
133+
def test_dict_equality(self):
134+
self.assertTrue({1: 'a', 2: 'b'} == {2: 'b', 1: 'a'})
135+
self.assertFalse({1: 'a'} == {1: 'b'})
136+
self.assertFalse({1: 'a'} == {2: 'a'})
137+
138+
139+
if __name__ == '__main__':
140+
unittest.main()

0 commit comments

Comments
 (0)