@@ -4885,155 +4885,83 @@ class A:
48854885 uops = get_opnames (ex )
48864886 self .assertNotIn ("_REPLACE_WITH_TRUE" , uops )
48874887
4888- def test_to_bool_kwargs_dict (self ):
4889- """**kwargs is known to be dict, so TO_BOOL specializes to _TO_BOOL_SIZED."""
4890- def inner (** kwargs ):
4888+ def test_to_bool_static_type (self ):
4889+ # *args is tuple and **kwargs is dict in tier2, so TO_BOOL on those
4890+ # locals specializes to _TO_BOOL_SIZED without a runtime guard.
4891+ # `va_kw_regular` also covers the non-zero slot-offset case.
4892+ def kw (** kwargs ):
48914893 cnt = 0
4892- for i in range (TIER2_THRESHOLD ):
4894+ for _ in range (TIER2_THRESHOLD ):
48934895 if kwargs :
48944896 cnt += 1
48954897 return cnt
48964898
4897- def f (n ):
4898- return inner (a = 1 , b = 2 )
4899-
4900- res , ex = self ._run_with_optimizer (f , TIER2_THRESHOLD )
4901- self .assertEqual (res , TIER2_THRESHOLD )
4902- ex_inner = get_first_executor (inner )
4903- self .assertIsNotNone (ex_inner )
4904- uops = get_opnames (ex_inner )
4905- self .assertIn ("_TO_BOOL_SIZED" , uops )
4906- self .assertNotIn ("_TO_BOOL" , uops )
4907-
4908- def test_to_bool_kwargs_empty_dict (self ):
4909- """**kwargs is known to be dict even when empty."""
4910- def inner (** kwargs ):
4899+ def va (* args ):
49114900 cnt = 0
4912- for i in range (TIER2_THRESHOLD ):
4913- if not kwargs :
4914- cnt += 1
4915- return cnt
4916-
4917- def f (n ):
4918- return inner ()
4919-
4920- res , ex = self ._run_with_optimizer (f , TIER2_THRESHOLD )
4921- self .assertEqual (res , TIER2_THRESHOLD )
4922- ex_inner = get_first_executor (inner )
4923- self .assertIsNotNone (ex_inner )
4924- uops = get_opnames (ex_inner )
4925- self .assertIn ("_TO_BOOL_SIZED" , uops )
4926- self .assertNotIn ("_TO_BOOL" , uops )
4927-
4928- def test_to_bool_varargs_tuple (self ):
4929- """*args is known to be tuple, so TO_BOOL specializes to _TO_BOOL_SIZED."""
4930- def inner (* args ):
4931- cnt = 0
4932- for i in range (TIER2_THRESHOLD ):
4901+ for _ in range (TIER2_THRESHOLD ):
49334902 if args :
49344903 cnt += 1
49354904 return cnt
49364905
4937- def f (n ):
4938- return inner (1 , 2 , 3 )
4939-
4940- res , ex = self ._run_with_optimizer (f , TIER2_THRESHOLD )
4941- self .assertEqual (res , TIER2_THRESHOLD )
4942- ex_inner = get_first_executor (inner )
4943- self .assertIsNotNone (ex_inner )
4944- uops = get_opnames (ex_inner )
4945- self .assertIn ("_TO_BOOL_SIZED" , uops )
4946- self .assertNotIn ("_TO_BOOL" , uops )
4947-
4948- def test_to_bool_varargs_empty_tuple (self ):
4949- """*args is known to be tuple even when empty."""
4950- def inner (* args ):
4906+ def va_kw_regular (x , y , * args , key = None , ** kwargs ):
49514907 cnt = 0
4952- for i in range (TIER2_THRESHOLD ):
4953- if not args :
4954- cnt += 1
4955- return cnt
4956-
4957- def f (n ):
4958- return inner ()
4959-
4960- res , ex = self ._run_with_optimizer (f , TIER2_THRESHOLD )
4961- self .assertEqual (res , TIER2_THRESHOLD )
4962- ex_inner = get_first_executor (inner )
4963- self .assertIsNotNone (ex_inner )
4964- uops = get_opnames (ex_inner )
4965- self .assertIn ("_TO_BOOL_SIZED" , uops )
4966- self .assertNotIn ("_TO_BOOL" , uops )
4967-
4968- def test_to_bool_args_and_kwargs (self ):
4969- """Combined *args and **kwargs both get correct types."""
4970- def inner (* args , ** kwargs ):
4971- cnt = 0
4972- for i in range (TIER2_THRESHOLD ):
4973- if args and kwargs :
4974- cnt += 1
4975- return cnt
4976-
4977- def f (n ):
4978- return inner (1 , 2 , a = 3 )
4979-
4980- res , ex = self ._run_with_optimizer (f , TIER2_THRESHOLD )
4981- self .assertEqual (res , TIER2_THRESHOLD )
4982- ex_inner = get_first_executor (inner )
4983- self .assertIsNotNone (ex_inner )
4984- uops = get_opnames (ex_inner )
4985- # Both the tuple (args) and dict (kwargs) TO_BOOLs specialize to _TO_BOOL_SIZED.
4986- self .assertGreaterEqual (count_ops (ex_inner , "_TO_BOOL_SIZED" ), 2 )
4987- self .assertNotIn ("_TO_BOOL" , uops )
4988-
4989- def test_to_bool_args_kwargs_with_regular_params (self ):
4990- """*args/**kwargs slot calculation is correct with regular params."""
4991- def inner (x , y , * args , key = None , ** kwargs ):
4992- cnt = 0
4993- for i in range (TIER2_THRESHOLD ):
4908+ for _ in range (TIER2_THRESHOLD ):
49944909 if args and kwargs :
49954910 cnt += 1
49964911 return cnt
49974912
4998- def f (n ):
4999- return inner (1 , 2 , 3 , 4 , key = "v" , extra = 5 )
5000-
5001- res , ex = self ._run_with_optimizer (f , TIER2_THRESHOLD )
5002- self .assertEqual (res , TIER2_THRESHOLD )
5003- ex_inner = get_first_executor (inner )
5004- self .assertIsNotNone (ex_inner )
5005- uops = get_opnames (ex_inner )
5006- self .assertGreaterEqual (count_ops (ex_inner , "_TO_BOOL_SIZED" ), 2 )
5007- self .assertNotIn ("_TO_BOOL" , uops )
5008-
5009- def test_to_bool_kwargs_only_no_varargs (self ):
5010- """**kwargs without *args gets correct dict type."""
5011- def inner (x , ** kwargs ):
5012- cnt = 0
5013- for i in range (TIER2_THRESHOLD ):
5014- if kwargs :
5015- cnt += 1
5016- return cnt
5017-
5018- def f (n ):
5019- return inner (1 , a = 2 )
5020-
5021- res , ex = self ._run_with_optimizer (f , TIER2_THRESHOLD )
5022- self .assertEqual (res , TIER2_THRESHOLD )
5023- ex_inner = get_first_executor (inner )
5024- self .assertIsNotNone (ex_inner )
5025- uops = get_opnames (ex_inner )
5026- self .assertIn ("_TO_BOOL_SIZED" , uops )
5027- self .assertNotIn ("_TO_BOOL" , uops )
4913+ # (inner, args, kwargs, min _TO_BOOL_SIZED count)
4914+ cases = [
4915+ (kw , (), {"a" : 1 , "b" : 2 }, 1 ),
4916+ (va , (1 , 2 , 3 ), {}, 1 ),
4917+ (va_kw_regular , (1 , 2 , 3 , 4 ), {"key" : "v" , "extra" : 5 }, 2 ),
4918+ ]
4919+ for inner , args , kwargs , expected in cases :
4920+ with self .subTest (case = inner .__name__ ):
4921+ inner (* args , ** kwargs )
4922+ ex_inner = get_first_executor (inner )
4923+ self .assertIsNotNone (ex_inner )
4924+ uops = get_opnames (ex_inner )
4925+ self .assertGreaterEqual (
4926+ count_ops (ex_inner , "_TO_BOOL_SIZED" ), expected )
4927+ self .assertNotIn ("_TO_BOOL" , uops )
4928+
4929+ def test_to_bool_recorded_type (self ):
4930+ # A value whose static type is unknown but whose runtime type is
4931+ # recorded gets a `_GUARD_TOS_<TYPE>` followed by _TO_BOOL_SIZED.
4932+ # A fresh `inner` is compiled per case so each gets its own executor.
4933+ src = (
4934+ "def inner(v):\n "
4935+ " cnt = 0\n "
4936+ " for _ in range(T):\n "
4937+ " if v:\n "
4938+ " cnt += 1\n "
4939+ " return cnt\n "
4940+ )
4941+ cases = [
4942+ (b"hello" , "_GUARD_TOS_BYTES" ),
4943+ (bytearray (b"hello" ), "_GUARD_TOS_BYTEARRAY" ),
4944+ (frozenset ({1 , 2 , 3 }), "_GUARD_TOS_FROZENSET" ),
4945+ (frozendict ({1 : 1 , 2 : 2 }), "_GUARD_TOS_FROZENDICT" ),
4946+ ]
4947+ for value , guard_op in cases :
4948+ with self .subTest (case = type (value ).__name__ ):
4949+ ns = {"T" : TIER2_THRESHOLD }
4950+ exec (src , ns )
4951+ inner = ns ["inner" ]
4952+ self .assertEqual (inner (value ), TIER2_THRESHOLD )
4953+ ex_inner = get_first_executor (inner )
4954+ self .assertIsNotNone (ex_inner )
4955+ uops = get_opnames (ex_inner )
4956+ self .assertIn (guard_op , uops )
4957+ self .assertIn ("_TO_BOOL_SIZED" , uops )
4958+ self .assertNotIn ("_TO_BOOL" , uops )
50284959
50294960 def test_to_bool_set_with_dummy_entries (self ):
5030- """Sets with dummy entries (after discard) must evaluate as falsy.
5031-
5032- PySetObject does not use PyObject_VAR_HEAD; reading ob_size at that
5033- offset accidentally reads `fill`, which counts both live *and* dummy
5034- (deleted) entries. A set whose items have all been discarded must
5035- still be falsy even after the JIT specialises TO_BOOL.
5036- """
4961+ # PySetObject does not use PyObject_VAR_HEAD; reading ob_size at that
4962+ # offset accidentally reads `fill`, which counts both live *and* dummy
4963+ # (deleted) entries. A set whose items have all been discarded must
4964+ # still be falsy even after the JIT specialises TO_BOOL.
50374965 def f (n ):
50384966 result = []
50394967 for _ in range (n ):
@@ -5043,59 +4971,8 @@ def f(n):
50434971 return result
50444972
50454973 res , ex = self ._run_with_optimizer (f , TIER2_THRESHOLD )
5046- # Every element must be False: the set is empty after discard()
50474974 self .assertTrue (all (r is False for r in res ))
50484975
5049- def _check_to_bool_recorded_type (self , inner , value , guard_op ):
5050- """Recorded-type path: a value whose static type is unknown but whose
5051- runtime type is recorded should emit `guard_op` + _TO_BOOL_SIZED."""
5052- self .assertEqual (inner (value ), TIER2_THRESHOLD )
5053- ex_inner = get_first_executor (inner )
5054- self .assertIsNotNone (ex_inner )
5055- uops = get_opnames (ex_inner )
5056- self .assertIn (guard_op , uops )
5057- self .assertIn ("_TO_BOOL_SIZED" , uops )
5058- self .assertNotIn ("_TO_BOOL" , uops )
5059-
5060- def test_to_bool_bytes (self ):
5061- def inner (v ):
5062- cnt = 0
5063- for _ in range (TIER2_THRESHOLD ):
5064- if v :
5065- cnt += 1
5066- return cnt
5067- self ._check_to_bool_recorded_type (inner , b"hello" , "_GUARD_TOS_BYTES" )
5068-
5069- def test_to_bool_bytearray (self ):
5070- def inner (v ):
5071- cnt = 0
5072- for _ in range (TIER2_THRESHOLD ):
5073- if v :
5074- cnt += 1
5075- return cnt
5076- self ._check_to_bool_recorded_type (
5077- inner , bytearray (b"hello" ), "_GUARD_TOS_BYTEARRAY" )
5078-
5079- def test_to_bool_frozenset (self ):
5080- def inner (v ):
5081- cnt = 0
5082- for _ in range (TIER2_THRESHOLD ):
5083- if v :
5084- cnt += 1
5085- return cnt
5086- self ._check_to_bool_recorded_type (
5087- inner , frozenset ({1 , 2 , 3 }), "_GUARD_TOS_FROZENSET" )
5088-
5089- def test_to_bool_frozendict (self ):
5090- def inner (v ):
5091- cnt = 0
5092- for _ in range (TIER2_THRESHOLD ):
5093- if v :
5094- cnt += 1
5095- return cnt
5096- self ._check_to_bool_recorded_type (
5097- inner , frozendict ({1 : 1 , 2 : 2 }), "_GUARD_TOS_FROZENDICT" )
5098-
50994976 def test_attr_promotion_failure (self ):
51004977 # We're not testing for any specific uops here, just
51014978 # testing it doesn't crash.
0 commit comments