@@ -1562,6 +1562,101 @@ def fn(a):
15621562
15631563 fn (A ())
15641564
1565+ def test_init_resolves_callable (self ):
1566+ """
1567+ _CHECK_AND_ALLOCATE_OBJECT should resolve __init__ to a constant,
1568+ enabling the optimizer to trace into the init frame and eliminate
1569+ redundant function version and arg count checks.
1570+ """
1571+ class MyPoint :
1572+ def __init__ (self , x , y ):
1573+ self .x = x
1574+ self .y = y
1575+
1576+ def testfunc (n ):
1577+ total = 0.0
1578+ for _ in range (n ):
1579+ p = MyPoint (1.0 , 2.0 )
1580+ total += p .x
1581+ return total
1582+
1583+ res , ex = self ._run_with_optimizer (testfunc , TIER2_THRESHOLD )
1584+ self .assertAlmostEqual (res , TIER2_THRESHOLD * 1.0 )
1585+ self .assertIsNotNone (ex )
1586+ uops = get_opnames (ex )
1587+ # The __init__ call should be traced through via _PUSH_FRAME
1588+ self .assertIn ("_PUSH_FRAME" , uops )
1589+ # __init__ resolution eliminates function version and arg checks
1590+ self .assertNotIn ("_CHECK_FUNCTION_VERSION" , uops )
1591+ self .assertNotIn ("_CHECK_FUNCTION_EXACT_ARGS" , uops )
1592+
1593+ def test_guard_type_version_locked_propagates (self ):
1594+ """
1595+ _GUARD_TYPE_VERSION_LOCKED should set the type version on the
1596+ symbol so repeated accesses to the same type can benefit.
1597+ """
1598+ class Item :
1599+ def __init__ (self , val ):
1600+ self .val = val
1601+
1602+ def get (self ):
1603+ return self .val
1604+
1605+ def get2 (self ):
1606+ return self .val + 1
1607+
1608+ def testfunc (n ):
1609+ item = Item (42 )
1610+ total = 0
1611+ for _ in range (n ):
1612+ # Two method calls on the same object — the second
1613+ # should benefit from type info set by the first.
1614+ total += item .get () + item .get2 ()
1615+ return total
1616+
1617+ res , ex = self ._run_with_optimizer (testfunc , TIER2_THRESHOLD )
1618+ self .assertEqual (res , TIER2_THRESHOLD * (42 + 43 ))
1619+ self .assertIsNotNone (ex )
1620+ uops = get_opnames (ex )
1621+ # Both methods should be traced through
1622+ self .assertEqual (uops .count ("_PUSH_FRAME" ), 2 )
1623+ # Type version propagation: one guard covers both method lookups
1624+ self .assertEqual (uops .count ("_GUARD_TYPE_VERSION" ), 1 )
1625+ # Function checks eliminated (type info resolves the callable)
1626+ self .assertNotIn ("_CHECK_FUNCTION_VERSION" , uops )
1627+ self .assertNotIn ("_CHECK_FUNCTION_EXACT_ARGS" , uops )
1628+
1629+ def test_method_chain_guard_elimination (self ):
1630+ """
1631+ Calling two methods on the same object should share the outer
1632+ type guard — only one _GUARD_TYPE_VERSION for the two lookups.
1633+ """
1634+ class Calc :
1635+ def __init__ (self , val ):
1636+ self .val = val
1637+
1638+ def add (self , x ):
1639+ self .val += x
1640+ return self
1641+
1642+ def testfunc (n ):
1643+ c = Calc (0 )
1644+ for _ in range (n ):
1645+ c .add (1 ).add (2 )
1646+ return c .val
1647+
1648+ res , ex = self ._run_with_optimizer (testfunc , TIER2_THRESHOLD )
1649+ self .assertEqual (res , TIER2_THRESHOLD * 3 )
1650+ self .assertIsNotNone (ex )
1651+ uops = get_opnames (ex )
1652+ # Both add() calls should be inlined
1653+ push_count = uops .count ("_PUSH_FRAME" )
1654+ self .assertEqual (push_count , 2 )
1655+ # Only one outer type version guard for the two method lookups
1656+ # on the same object c (the second lookup reuses type info)
1657+ guard_version_count = uops .count ("_GUARD_TYPE_VERSION" )
1658+ self .assertEqual (guard_version_count , 1 )
1659+
15651660 def test_func_guards_removed_or_reduced (self ):
15661661 def testfunc (n ):
15671662 for i in range (n ):
0 commit comments