Skip to content

Commit c7daa03

Browse files
eendebakptclaude
andcommitted
Restore test_opt.py from origin/main, re-applying TO_BOOL tests
Earlier merge resolutions silently dropped a sizable set of tests that exist on origin/main (test_get_iter_list, test_get_iter_gen, test_get_iter_trad, test_cached_load_special, all test_binary_op_extend_*, test_contains_op_frozendict_const_fold, test_not_contains_op_frozendict_const_fold, test_load_attr_getattribute_frame, etc.). Restore the file from origin/main and re-insert the eight TO_BOOL tests that are unique to this branch (test_to_bool_kwargs_dict, _empty_dict, _varargs_tuple, _empty_tuple, _args_and_kwargs, _args_kwargs_with_regular_params, _kwargs_only_no_varargs, _set_with_dummy_entries) right after test_to_bool_always_true. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ee0325c commit c7daa03

1 file changed

Lines changed: 291 additions & 1 deletion

File tree

Lib/test/test_capi/test_opt.py

Lines changed: 291 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,75 @@ def testfunc(n):
371371
# look for indirect evidence: the += operator
372372
self.assertIn("_BINARY_OP_ADD_INT", uops)
373373

374+
def test_get_iter_list(self):
375+
l = list(range(10))
376+
def testfunc(n):
377+
total = 0
378+
while n:
379+
n -= 1
380+
total += n
381+
for i in l:
382+
break
383+
return total
384+
385+
total = testfunc(TIER2_THRESHOLD)
386+
self.assertEqual(total, sum(range(TIER2_THRESHOLD)))
387+
ex = get_first_executor(testfunc)
388+
self.assertIsNotNone(ex)
389+
uops = get_opnames(ex)
390+
self.assertIn("_PUSH_TAGGED_ZERO", uops)
391+
self.assertNotIn("_GET_ITER", uops)
392+
self.assertNotIn("_GET_ITER_TRAD", uops)
393+
self.assertNotIn("_GET_ITER_VIRTUAL", uops)
394+
self.assertNotIn("_GET_ITER_SELF", uops)
395+
396+
def test_get_iter_gen(self):
397+
def gen():
398+
while True:
399+
yield 1
400+
401+
def testfunc(n):
402+
total = 0
403+
while n:
404+
n -= 1
405+
total += n
406+
for i in gen():
407+
break
408+
return total
409+
410+
total = testfunc(TIER2_THRESHOLD)
411+
self.assertEqual(total, sum(range(TIER2_THRESHOLD)))
412+
ex = get_first_executor(testfunc)
413+
self.assertIsNotNone(ex)
414+
uops = get_opnames(ex)
415+
self.assertIn("_PUSH_NULL", uops)
416+
self.assertNotIn("_GET_ITER", uops)
417+
self.assertNotIn("_GET_ITER_TRAD", uops)
418+
self.assertNotIn("_GET_ITER_VIRTUAL", uops)
419+
self.assertNotIn("_GET_ITER_SELF", uops)
420+
421+
def test_get_iter_trad(self):
422+
d = {v:v for v in range(10)}
423+
def testfunc(n):
424+
total = 0
425+
while n:
426+
n -= 1
427+
total += n
428+
for i in d:
429+
break
430+
return total
431+
432+
total = testfunc(TIER2_THRESHOLD)
433+
self.assertEqual(total, sum(range(TIER2_THRESHOLD)))
434+
ex = get_first_executor(testfunc)
435+
self.assertIsNotNone(ex)
436+
uops = get_opnames(ex)
437+
self.assertIn("_GET_ITER_TRAD", uops)
438+
self.assertNotIn("_GET_ITER", uops)
439+
self.assertNotIn("_GET_ITER_VIRTUAL", uops)
440+
self.assertNotIn("_GET_ITER_SELF", uops)
441+
442+
374443
def test_for_iter_range(self):
375444
def testfunc(n):
376445
total = 0
@@ -2646,6 +2715,9 @@ def testfunc(n):
26462715
uops = get_opnames(ex)
26472716
# When the result of type(...) is known, _CALL_TYPE_1 is decomposed.
26482717
self.assertNotIn("_CALL_TYPE_1", uops)
2718+
# _CALL_TYPE_1 produces 2 _POP_TOP_NOP (callable and null)
2719+
# type(42) is int produces 4 _POP_TOP_NOP
2720+
self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 6)
26492721

26502722
def test_call_type_1_result_is_const(self):
26512723
def testfunc(n):
@@ -3439,6 +3511,29 @@ def f(n):
34393511
self.assertNotIn("_LOAD_ATTR_METHOD_NO_DICT", uops)
34403512
self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)
34413513

3514+
def test_cached_load_special(self):
3515+
class CM:
3516+
def __enter__(self):
3517+
return self
3518+
def __exit__(self, *args):
3519+
pass
3520+
def f(n):
3521+
cm = CM()
3522+
x = 0
3523+
for _ in range(n):
3524+
with cm:
3525+
x += 1
3526+
return x
3527+
res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD)
3528+
self.assertIsNotNone(ex)
3529+
self.assertEqual(res, TIER2_THRESHOLD)
3530+
uops = get_opnames(ex)
3531+
self.assertNotIn("_LOAD_SPECIAL", uops)
3532+
# __enter__/__exit__ produce 2 _POP_TOP_NOP
3533+
# x += 1 produces 2 _POP_TOP_NOP
3534+
# __exit__()'s None return produces 1 _POP_TOP_NOP
3535+
self.assertGreaterEqual(count_ops(ex, "_POP_TOP_NOP"), 5)
3536+
34423537
def test_store_fast_refcount_elimination(self):
34433538
def foo(x):
34443539
# Since x is known to be
@@ -4277,6 +4372,118 @@ def testfunc(n):
42774372
# propagates PyFloat_Type.
42784373
self.assertNotIn("_GUARD_NOS_FLOAT", uops)
42794374

4375+
def test_binary_op_extend_list_concat_type_propagation(self):
4376+
# list + list is specialized via BINARY_OP_EXTEND. The tier 2 optimizer
4377+
# should learn that the result is a list and eliminate subsequent
4378+
# list-type guards.
4379+
def testfunc(n):
4380+
a = [1, 2]
4381+
b = [3, 4]
4382+
x = True
4383+
for _ in range(n):
4384+
c = a + b
4385+
if c[0]:
4386+
x = False
4387+
return x
4388+
4389+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
4390+
self.assertEqual(res, False)
4391+
self.assertIsNotNone(ex)
4392+
uops = get_opnames(ex)
4393+
self.assertIn("_BINARY_OP_EXTEND", uops)
4394+
# The c[0] subscript emits _GUARD_NOS_LIST before _BINARY_OP_SUBSCR_LIST_INT;
4395+
# since _BINARY_OP_EXTEND now propagates PyList_Type, that guard is gone.
4396+
self.assertIn("_BINARY_OP_SUBSCR_LIST_INT", uops)
4397+
self.assertNotIn("_GUARD_NOS_LIST", uops)
4398+
4399+
def test_binary_op_extend_tuple_concat_type_propagation(self):
4400+
# tuple + tuple is specialized via BINARY_OP_EXTEND. The tier 2 optimizer
4401+
# should learn the result is a tuple and eliminate subsequent tuple guards.
4402+
def testfunc(n):
4403+
t1 = (1, 2)
4404+
t2 = (3, 4)
4405+
for _ in range(n):
4406+
a, b, c, d = t1 + t2
4407+
return a + b + c + d
4408+
4409+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
4410+
self.assertEqual(res, 10)
4411+
self.assertIsNotNone(ex)
4412+
uops = get_opnames(ex)
4413+
self.assertIn("_BINARY_OP_EXTEND", uops)
4414+
self.assertIn("_UNPACK_SEQUENCE_TUPLE", uops)
4415+
self.assertNotIn("_GUARD_TOS_TUPLE", uops)
4416+
4417+
def test_binary_op_extend_guard_elimination(self):
4418+
# When both operands have known types (e.g., from a prior
4419+
# _BINARY_OP_EXTEND result), the _GUARD_BINARY_OP_EXTEND
4420+
# should be eliminated.
4421+
def testfunc(n):
4422+
a = [1, 2]
4423+
b = [3, 4]
4424+
total = 0
4425+
for _ in range(n):
4426+
c = a + b # first: guard stays, result type = list
4427+
d = c + c # second: both operands are list -> guard eliminated
4428+
total += d[0]
4429+
return total
4430+
4431+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
4432+
self.assertEqual(res, TIER2_THRESHOLD)
4433+
self.assertIsNotNone(ex)
4434+
uops = get_opnames(ex)
4435+
# Both list additions use _BINARY_OP_EXTEND
4436+
self.assertEqual(uops.count("_BINARY_OP_EXTEND"), 2)
4437+
# But the second guard is eliminated because both operands
4438+
# are known to be lists from the first _BINARY_OP_EXTEND.
4439+
self.assertEqual(uops.count("_GUARD_BINARY_OP_EXTEND"), 1)
4440+
4441+
def test_binary_op_extend_partial_guard_lhs_known(self):
4442+
# When the lhs type is already known (from a prior _BINARY_OP_EXTEND
4443+
# result) but the rhs type is not, the optimizer should emit
4444+
# _GUARD_BINARY_OP_EXTEND_RHS (checking only the rhs) instead of
4445+
# the full _GUARD_BINARY_OP_EXTEND.
4446+
def testfunc(n):
4447+
a = [1, 2]
4448+
b = [3, 4]
4449+
total = 0
4450+
for _ in range(n):
4451+
c = a + b # result type is list (known)
4452+
d = c + b # lhs (c) is known list, rhs (b) is not -> _GUARD_BINARY_OP_EXTEND_RHS
4453+
total += d[0]
4454+
return total
4455+
4456+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
4457+
self.assertEqual(res, TIER2_THRESHOLD)
4458+
self.assertIsNotNone(ex)
4459+
uops = get_opnames(ex)
4460+
self.assertIn("_BINARY_OP_EXTEND", uops)
4461+
self.assertIn("_GUARD_BINARY_OP_EXTEND_RHS", uops)
4462+
self.assertNotIn("_GUARD_BINARY_OP_EXTEND_LHS", uops)
4463+
4464+
def test_binary_op_extend_partial_guard_rhs_known(self):
4465+
# When the rhs type is already known (from a prior _BINARY_OP_EXTEND
4466+
# result) but the lhs type is not, the optimizer should emit
4467+
# _GUARD_BINARY_OP_EXTEND_LHS (checking only the lhs) instead of
4468+
# the full _GUARD_BINARY_OP_EXTEND.
4469+
def testfunc(n):
4470+
a = [1, 2]
4471+
b = [3, 4]
4472+
total = 0
4473+
for _ in range(n):
4474+
c = a + b # result type is list (known)
4475+
d = b + c # rhs (c) is known list, lhs (b) is not -> _GUARD_BINARY_OP_EXTEND_LHS
4476+
total += d[2]
4477+
return total
4478+
4479+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
4480+
self.assertEqual(res, TIER2_THRESHOLD)
4481+
self.assertIsNotNone(ex)
4482+
uops = get_opnames(ex)
4483+
self.assertIn("_BINARY_OP_EXTEND", uops)
4484+
self.assertIn("_GUARD_BINARY_OP_EXTEND_LHS", uops)
4485+
self.assertNotIn("_GUARD_BINARY_OP_EXTEND_RHS", uops)
4486+
42804487
def test_unary_invert_long_type(self):
42814488
def testfunc(n):
42824489
for _ in range(n):
@@ -5008,7 +5215,7 @@ def testfunc(n):
50085215
self.assertEqual(res, TIER2_THRESHOLD)
50095216
self.assertIsNotNone(ex)
50105217
uops = get_opnames(ex)
5011-
self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)
5218+
self.assertGreaterEqual(count_ops(ex, "_LOAD_CONST_INLINE_BORROW"), 2)
50125219
self.assertNotIn("_BINARY_OP_SUBSCR_DICT", uops)
50135220

50145221
def test_binary_subscr_frozendict_const_fold(self):
@@ -5023,6 +5230,7 @@ def testfunc(n):
50235230
self.assertEqual(res, TIER2_THRESHOLD)
50245231
self.assertIsNotNone(ex)
50255232
uops = get_opnames(ex)
5233+
self.assertGreaterEqual(count_ops(ex, "_LOAD_CONST_INLINE_BORROW"), 3)
50265234
# lookup result is folded to constant 1, so comparison is optimized away
50275235
self.assertNotIn("_COMPARE_OP_INT", uops)
50285236

@@ -5038,8 +5246,39 @@ def testfunc(n):
50385246
self.assertEqual(res, TIER2_THRESHOLD)
50395247
self.assertIsNotNone(ex)
50405248
uops = get_opnames(ex)
5249+
self.assertGreaterEqual(count_ops(ex, "_LOAD_CONST_INLINE_BORROW"), 3)
50415250
self.assertNotIn("_CONTAINS_OP_SET", uops)
50425251

5252+
def test_contains_op_frozendict_const_fold(self):
5253+
def testfunc(n):
5254+
x = 0
5255+
for _ in range(n):
5256+
if 'x' in FROZEN_DICT_CONST:
5257+
x += 1
5258+
return x
5259+
5260+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
5261+
self.assertEqual(res, TIER2_THRESHOLD)
5262+
self.assertIsNotNone(ex)
5263+
uops = get_opnames(ex)
5264+
self.assertGreaterEqual(count_ops(ex, "_LOAD_CONST_INLINE_BORROW"), 3)
5265+
self.assertNotIn("_CONTAINS_OP_DICT", uops)
5266+
5267+
def test_not_contains_op_frozendict_const_fold(self):
5268+
def testfunc(n):
5269+
x = 0
5270+
for _ in range(n):
5271+
if 'z' not in FROZEN_DICT_CONST:
5272+
x += 1
5273+
return x
5274+
5275+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
5276+
self.assertEqual(res, TIER2_THRESHOLD)
5277+
self.assertIsNotNone(ex)
5278+
uops = get_opnames(ex)
5279+
self.assertGreaterEqual(count_ops(ex, "_LOAD_CONST_INLINE_BORROW"), 3)
5280+
self.assertNotIn("_CONTAINS_OP_DICT", uops)
5281+
50435282
def test_binary_subscr_list_slice(self):
50445283
def testfunc(n):
50455284
x = 0
@@ -5258,6 +5497,57 @@ def testfunc(*args):
52585497
# This is a sign the optimizer ran and didn't hit contradiction.
52595498
self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)
52605499

5500+
def test_load_attr_getattribute_frame(self):
5501+
class B:
5502+
def __getattribute__(self, name):
5503+
return len(name)
5504+
5505+
def testfunc(n):
5506+
b = B()
5507+
y = 0
5508+
for _ in range(n):
5509+
y += b.x + b.y
5510+
return y
5511+
5512+
res, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
5513+
self.assertIsNotNone(ex)
5514+
self.assertEqual(res, 2 * TIER2_THRESHOLD)
5515+
uops = get_opnames(ex)
5516+
self.assertIn("_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN_FRAME", uops)
5517+
self.assertNotIn("_LOAD_GLOBAL_BUILTINS", uops)
5518+
5519+
def test_load_attr_property_frame_invalidates_on_code_change(self):
5520+
class C:
5521+
@property
5522+
def val(self):
5523+
return int(1)
5524+
5525+
fget = C.val.fget
5526+
5527+
def testfunc(*args):
5528+
n, c = args[0]
5529+
total = 0
5530+
for _ in range(n):
5531+
total += c.val
5532+
return total
5533+
5534+
testfunc((3, C()))
5535+
res, ex = self._run_with_optimizer(testfunc, (TIER2_THRESHOLD, C()))
5536+
self.assertEqual(res, TIER2_THRESHOLD)
5537+
self.assertIsNotNone(ex)
5538+
uops = get_opnames(ex)
5539+
self.assertIn("_LOAD_ATTR_PROPERTY_FRAME", uops)
5540+
# Check the optimizer traced through the property call.
5541+
self.assertNotIn("_LOAD_GLOBAL_BUILTINS", uops)
5542+
self.assertIn("_CALL_BUILTIN_CLASS", uops)
5543+
5544+
fget.__code__ = (lambda self: 2).__code__
5545+
_testinternalcapi.clear_executor_deletion_list()
5546+
ex = get_first_executor(testfunc)
5547+
self.assertIsNone(ex)
5548+
res = testfunc((TIER2_THRESHOLD, C()))
5549+
self.assertEqual(res, TIER2_THRESHOLD * 2)
5550+
52615551
def test_unary_negative(self):
52625552
def testfunc(n):
52635553
a = 3

0 commit comments

Comments
 (0)