From e78ffee141c985c847e74a7419594803cf6c1116 Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Mon, 4 May 2026 17:28:18 +0200 Subject: [PATCH 1/8] [mypyc] Fix crash on accessing StopAsyncIteration --- mypyc/codegen/emit.py | 4 +++- mypyc/irbuild/expression.py | 5 ++++- mypyc/irbuild/function.py | 7 +++++-- mypyc/irbuild/vec.py | 2 +- mypyc/primitives/misc_ops.py | 5 ++++- mypyc/primitives/registry.py | 15 ++++++++++++--- mypyc/test-data/fixtures/ir.py | 3 +++ 7 files changed, 32 insertions(+), 9 deletions(-) diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index 54e77836a76ca..c32d3650a7f7a 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -1171,7 +1171,9 @@ def vec_item_type_c(self, typ: RVec) -> str: def type_c_ptr(self, typ: RPrimitive | RInstance) -> str | None: if isinstance(typ, RPrimitive) and typ.is_refcounted: - return "&" + builtin_names[typ.name][1] + builtin = builtin_names[typ.name] + prefix = "" if builtin.is_ptr else "&" + return prefix + builtin.src elif isinstance(typ, RInstance): return self.type_struct_name(typ.class_ir) return None diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 6ef59559c0112..0ee665ab10e72 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -69,6 +69,7 @@ ComparisonOp, Integer, LoadAddress, + LoadGlobal, LoadLiteral, PrimitiveDescription, RaiseStandardError, @@ -158,7 +159,9 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value: return builder.none(expr.line) fullname = expr.node.fullname if fullname in builtin_names: - typ, src = builtin_names[fullname] + typ, src, is_ptr = builtin_names[fullname] + if is_ptr: + return builder.add(LoadGlobal(typ, src, expr.line)) return builder.add(LoadAddress(typ, src, expr.line)) # special cases if fullname == "builtins.None": diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index 0cf0edb32f86c..e1c03601bf870 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -46,6 +46,7 @@ GetAttr, Integer, LoadAddress, + LoadGlobal, LoadLiteral, Register, Return, @@ -936,7 +937,9 @@ def load_type(builder: IRBuilder, typ: TypeInfo, unbounded_type: Type | None, li class_ir = builder.mapper.type_to_ir[typ] class_obj = builder.builder.get_native_type(class_ir) elif typ.fullname in builtin_names: - builtin_addr_type, src = builtin_names[typ.fullname] + builtin_addr_type, src, is_ptr = builtin_names[typ.fullname] + if is_ptr: + class_obj = builder.add(LoadGlobal(builtin_addr_type, src, line)) class_obj = builder.add(LoadAddress(builtin_addr_type, src, line)) elif isinstance(unbounded_type, UnboundType): path_parts = unbounded_type.name.split(".") @@ -1013,7 +1016,7 @@ def gen_native_func_call_and_return(fdef: FuncDef) -> None: coerced = builder.coerce(ret_val, current_func_decl.sig.ret_type, line) builder.add(Return(coerced)) - typ, src = builtin_names["builtins.int"] + typ, src, _ = builtin_names["builtins.int"] int_type_obj = builder.add(LoadAddress(typ, src, line)) is_int = builder.builder.type_is_op(impl_to_use, int_type_obj, line) diff --git a/mypyc/irbuild/vec.py b/mypyc/irbuild/vec.py index 128c0f62c0bf0..207ce8dcfce53 100644 --- a/mypyc/irbuild/vec.py +++ b/mypyc/irbuild/vec.py @@ -213,7 +213,7 @@ def vec_item_type_info( builder: LowLevelIRBuilder, typ: RType, line: int ) -> tuple[Value | None, bool, int]: if isinstance(typ, RPrimitive) and typ.is_refcounted: - typ, src = builtin_names[typ.name] + typ, src, _ = builtin_names[typ.name] return builder.load_address(src, typ), False, 0 elif isinstance(typ, RInstance): return builder.load_native_type_object(typ.name), False, 0 diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index 6be74baff3d0b..aadae7b3a7d72 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -53,7 +53,10 @@ # Get the boxed StopAsyncIteration object stop_async_iteration_op = load_address_op( - name="builtins.StopAsyncIteration", type=object_rprimitive, src="PyExc_StopAsyncIteration" + name="builtins.StopAsyncIteration", + type=object_rprimitive, + src="PyExc_StopAsyncIteration", + is_ptr=True, ) # id(obj) diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 2376ddf9fbd0d..cad629dad39f1 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -85,7 +85,14 @@ class LoadAddressDescription(NamedTuple): # Primitive ops for unary ops unary_ops: dict[str, list[PrimitiveDescription]] = {} -builtin_names: dict[str, tuple[RType, str]] = {} + +class BuiltInName(NamedTuple): + type: RType + src: str # name of the target to load + is_ptr: bool # whether the target is already a pointer (PyObject *) + + +builtin_names: dict[str, BuiltInName] = {} def method_op( @@ -381,9 +388,11 @@ def unary_op( return desc -def load_address_op(name: str, type: RType, src: str) -> LoadAddressDescription: +def load_address_op( + name: str, type: RType, src: str, *, is_ptr: bool = False +) -> LoadAddressDescription: assert name not in builtin_names, "already defined: %s" % name - builtin_names[name] = (type, src) + builtin_names[name] = BuiltInName(type, src, is_ptr) return LoadAddressDescription(name, type, src) diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 60f06b12f2959..101c54ad7eff0 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -374,6 +374,9 @@ class ReferenceError(Exception): pass class StopIteration(Exception): value: Any +class StopAsyncIteration(Exception): + value: Any + class ArithmeticError(Exception): pass class ZeroDivisionError(ArithmeticError): pass class OverflowError(ArithmeticError): pass From b4f46eb66c9258887975089059ba697043b1c2f0 Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Mon, 4 May 2026 17:39:48 +0200 Subject: [PATCH 2/8] Add test --- mypyc/test-data/run-async.test | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test index 6127a0dd47a33..96e8b8055e3fe 100644 --- a/mypyc/test-data/run-async.test +++ b/mypyc/test-data/run-async.test @@ -1874,3 +1874,26 @@ def test_nested_coroutine_calls_another_nested_function(): from typing import Any, Generator def run(x: object) -> object: ... + +[case testRaiseStopAsyncIteration] +from async_iter import async_iter +from testutil import assertRaises + +async def test_iteration() -> None: + new_list: list[int] = [] + async for v in async_iter([1, 2, 3]): + new_list.append(v) + + assert new_list == [1, 2, 3] + + with assertRaises(StopAsyncIteration): + await async_iter([]).__anext__() + +[file async_iter.py] +from typing import AsyncIterator + +async def async_iter(vals: list[int]) -> AsyncIterator[int]: + for v in vals: + yield v + +[typing fixtures/typing-full.pyi] From 3d94ed7503619d126fc7e84df86f75c4f74c9d5b Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Mon, 4 May 2026 18:07:03 +0200 Subject: [PATCH 3/8] Add helper functions in Builders --- mypyc/irbuild/builder.py | 5 ++++- mypyc/irbuild/expression.py | 6 +----- mypyc/irbuild/function.py | 10 ++-------- mypyc/irbuild/ll_builder.py | 13 +++++++++++-- mypyc/irbuild/vec.py | 3 +-- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index c7f3748e8f225..f6742dced7f4e 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -153,7 +153,7 @@ import_op, native_import_op, ) -from mypyc.primitives.registry import CFunctionDescription, function_ops +from mypyc.primitives.registry import BuiltInName, CFunctionDescription, function_ops from mypyc.primitives.tuple_ops import tuple_get_item_unsafe_op # These int binary operations can borrow their operands safely, since the @@ -1603,6 +1603,9 @@ def add_coroutine_setup_call(self, class_name: str, obj: Value) -> Value: ) ) + def load_builtin(self, builtin: BuiltInName, line: int) -> Value: + return self.builder.load_builtin(builtin, line) + def gen_arg_defaults(builder: IRBuilder) -> None: """Generate blocks for arguments that have default values. diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 0ee665ab10e72..6dc22d053b2b4 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -69,7 +69,6 @@ ComparisonOp, Integer, LoadAddress, - LoadGlobal, LoadLiteral, PrimitiveDescription, RaiseStandardError, @@ -159,10 +158,7 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value: return builder.none(expr.line) fullname = expr.node.fullname if fullname in builtin_names: - typ, src, is_ptr = builtin_names[fullname] - if is_ptr: - return builder.add(LoadGlobal(typ, src, expr.line)) - return builder.add(LoadAddress(typ, src, expr.line)) + return builder.load_builtin(builtin_names[fullname], expr.line) # special cases if fullname == "builtins.None": return builder.none(expr.line) diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index e1c03601bf870..8fe70d0d3a363 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -45,8 +45,6 @@ ComparisonOp, GetAttr, Integer, - LoadAddress, - LoadGlobal, LoadLiteral, Register, Return, @@ -937,10 +935,7 @@ def load_type(builder: IRBuilder, typ: TypeInfo, unbounded_type: Type | None, li class_ir = builder.mapper.type_to_ir[typ] class_obj = builder.builder.get_native_type(class_ir) elif typ.fullname in builtin_names: - builtin_addr_type, src, is_ptr = builtin_names[typ.fullname] - if is_ptr: - class_obj = builder.add(LoadGlobal(builtin_addr_type, src, line)) - class_obj = builder.add(LoadAddress(builtin_addr_type, src, line)) + class_obj = builder.load_builtin(builtin_names[typ.fullname], line) elif isinstance(unbounded_type, UnboundType): path_parts = unbounded_type.name.split(".") class_obj = builder.load_global_str(path_parts[0], line) @@ -1016,8 +1011,7 @@ def gen_native_func_call_and_return(fdef: FuncDef) -> None: coerced = builder.coerce(ret_val, current_func_decl.sig.ret_type, line) builder.add(Return(coerced)) - typ, src, _ = builtin_names["builtins.int"] - int_type_obj = builder.add(LoadAddress(typ, src, line)) + int_type_obj = builder.load_builtin(builtin_names["builtins.int"], line) is_int = builder.builder.type_is_op(impl_to_use, int_type_obj, line) native_call, non_native_call = BasicBlock(), BasicBlock() diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 1a79fce59ab12..c7c1c0a944343 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -183,6 +183,7 @@ ) from mypyc.primitives.registry import ( ERR_NEG_INT, + BuiltInName, CFunctionDescription, binary_ops, function_ops, @@ -323,8 +324,16 @@ def set_mem(self, ptr: Value, value_type: RType, value: Value) -> None: def get_element(self, reg: Value, field: str) -> Value: return self.add(GetElement(reg, field)) - def load_address(self, name: str, rtype: RType) -> Value: - return self.add(LoadAddress(rtype, name)) + def load_address(self, name: str, rtype: RType, line: int = -1) -> Value: + return self.add(LoadAddress(rtype, name, line)) + + def load_global(self, name: str, rtype: RType, line: int) -> Value: + return self.add(LoadGlobal(rtype, name, line)) + + def load_builtin(self, builtin: BuiltInName, line: int) -> Value: + if builtin.is_ptr: + return self.load_global(builtin.src, builtin.type, line) + return self.load_address(builtin.src, builtin.type, line) def load_struct_field( self, ptr: Value, struct: RStruct, field: str, *, borrow: bool = False diff --git a/mypyc/irbuild/vec.py b/mypyc/irbuild/vec.py index 207ce8dcfce53..19d607b197a0b 100644 --- a/mypyc/irbuild/vec.py +++ b/mypyc/irbuild/vec.py @@ -213,8 +213,7 @@ def vec_item_type_info( builder: LowLevelIRBuilder, typ: RType, line: int ) -> tuple[Value | None, bool, int]: if isinstance(typ, RPrimitive) and typ.is_refcounted: - typ, src, _ = builtin_names[typ.name] - return builder.load_address(src, typ), False, 0 + return builder.load_builtin(builtin_names[typ.name], line), False, 0 elif isinstance(typ, RInstance): return builder.load_native_type_object(typ.name), False, 0 elif typ in vec_item_type_tags: From b7ce86cd770f1c32a2323f8b46abfe0be6583fed Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Tue, 5 May 2026 12:28:59 +0200 Subject: [PATCH 4/8] Simplify ForAsyncIterable --- mypyc/irbuild/for_helpers.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 33a716624b178..95894dcedaba8 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -36,8 +36,8 @@ IntOp, LoadAddress, LoadErrorValue, + LoadGlobal, LoadLiteral, - LoadMem, MethodCall, RaiseStandardError, Register, @@ -63,7 +63,6 @@ is_tuple_rprimitive, object_pointer_rprimitive, object_rprimitive, - pointer_rprimitive, short_int_rprimitive, ) from mypyc.irbuild.builder import IRBuilder @@ -828,8 +827,9 @@ def gen_condition(self) -> None: line = self.line def except_match() -> Value: - addr = builder.add(LoadAddress(pointer_rprimitive, stop_async_iteration_op.src, line)) - return builder.add(LoadMem(stop_async_iteration_op.type, addr, borrow=True)) + return builder.add( + LoadGlobal(stop_async_iteration_op.type, stop_async_iteration_op.src, line) + ) def try_body() -> None: awaitable = builder.call_c(anext_op, [builder.read(self.iter_target, line)], line) From f367617839cd4c3c9b6e988a743e7f53ac99abb3 Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Tue, 5 May 2026 12:49:12 +0200 Subject: [PATCH 5/8] Expand the test --- mypyc/test-data/run-async.test | 47 ++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test index 96e8b8055e3fe..e24271b94f098 100644 --- a/mypyc/test-data/run-async.test +++ b/mypyc/test-data/run-async.test @@ -1879,13 +1879,56 @@ def run(x: object) -> object: ... from async_iter import async_iter from testutil import assertRaises -async def test_iteration() -> None: +class AsyncIter: + def __init__(self, vals: list[str]) -> None: + self._iter = iter(vals) + + def __aiter__(self) -> AsyncIter: + return self + + async def __anext__(self) -> str: + try: + return next(self._iter) + except StopIteration: + raise StopAsyncIteration + +async def test_iterator() -> None: new_list: list[int] = [] async for v in async_iter([1, 2, 3]): new_list.append(v) - assert new_list == [1, 2, 3] + new_list = [] + iter = async_iter([1, 2, 3]) + while True: + try: + v = await iter.__anext__() + new_list.append(v) + except StopAsyncIteration: + new_list.append(4) + break + assert new_list == [1, 2, 3, 4] + + with assertRaises(StopAsyncIteration): + await async_iter([]).__anext__() + +async def test_wrapper() -> None: + new_list: list[str] = [] + async for v in AsyncIter(['a', 'b', 'c']): + new_list.append(v) + assert new_list == ['a', 'b', 'c'] + + new_list = [] + iter = AsyncIter(['a', 'b', 'c']) + while True: + try: + v = await iter.__anext__() + new_list.append(v) + except StopAsyncIteration: + new_list.append('d') + break + assert new_list == ['a', 'b', 'c', 'd'] + with assertRaises(StopAsyncIteration): await async_iter([]).__anext__() From 3e04765851632becbf8e71c18cdb4b0e0a30b3bd Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Tue, 5 May 2026 16:37:27 +0200 Subject: [PATCH 6/8] Use separate dict for pointer variables --- mypyc/codegen/emit.py | 4 +--- mypyc/irbuild/builder.py | 6 +++--- mypyc/irbuild/expression.py | 5 ++--- mypyc/irbuild/function.py | 8 ++++---- mypyc/irbuild/ll_builder.py | 13 ++++++++----- mypyc/irbuild/vec.py | 3 +-- mypyc/primitives/misc_ops.py | 8 +++----- mypyc/primitives/registry.py | 23 ++++++++++++----------- 8 files changed, 34 insertions(+), 36 deletions(-) diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index c32d3650a7f7a..54e77836a76ca 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -1171,9 +1171,7 @@ def vec_item_type_c(self, typ: RVec) -> str: def type_c_ptr(self, typ: RPrimitive | RInstance) -> str | None: if isinstance(typ, RPrimitive) and typ.is_refcounted: - builtin = builtin_names[typ.name] - prefix = "" if builtin.is_ptr else "&" - return prefix + builtin.src + return "&" + builtin_names[typ.name][1] elif isinstance(typ, RInstance): return self.type_struct_name(typ.class_ir) return None diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index f6742dced7f4e..67aa24b3641c8 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -153,7 +153,7 @@ import_op, native_import_op, ) -from mypyc.primitives.registry import BuiltInName, CFunctionDescription, function_ops +from mypyc.primitives.registry import CFunctionDescription, function_ops from mypyc.primitives.tuple_ops import tuple_get_item_unsafe_op # These int binary operations can borrow their operands safely, since the @@ -1603,8 +1603,8 @@ def add_coroutine_setup_call(self, class_name: str, obj: Value) -> Value: ) ) - def load_builtin(self, builtin: BuiltInName, line: int) -> Value: - return self.builder.load_builtin(builtin, line) + def load_builtin(self, name: str, line: int) -> Value | None: + return self.builder.load_builtin(name, line) def gen_arg_defaults(builder: IRBuilder) -> None: diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 6dc22d053b2b4..e8d22a051cc4d 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -136,7 +136,6 @@ from mypyc.primitives.generic_ops import iter_op, name_op from mypyc.primitives.list_ops import list_append_op, list_extend_op, list_slice_op from mypyc.primitives.misc_ops import ellipsis_op, get_module_dict_op, new_slice_op, type_op -from mypyc.primitives.registry import builtin_names from mypyc.primitives.set_ops import set_add_op, set_in_op, set_update_op from mypyc.primitives.str_ops import str_slice_op from mypyc.primitives.tuple_ops import list_tuple_op, tuple_slice_op @@ -157,8 +156,8 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value: ) return builder.none(expr.line) fullname = expr.node.fullname - if fullname in builtin_names: - return builder.load_builtin(builtin_names[fullname], expr.line) + if builtin := builder.load_builtin(fullname, expr.line): + return builtin # special cases if fullname == "builtins.None": return builder.none(expr.line) diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index 8fe70d0d3a363..ef7450aeab24f 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -84,7 +84,6 @@ ) from mypyc.primitives.generic_ops import generic_getattr, generic_setattr, py_setattr_op from mypyc.primitives.misc_ops import register_function -from mypyc.primitives.registry import builtin_names from mypyc.sametype import is_same_method_signature, is_same_type # Top-level transform functions @@ -934,8 +933,8 @@ def load_type(builder: IRBuilder, typ: TypeInfo, unbounded_type: Type | None, li if typ in builder.mapper.type_to_ir: class_ir = builder.mapper.type_to_ir[typ] class_obj = builder.builder.get_native_type(class_ir) - elif typ.fullname in builtin_names: - class_obj = builder.load_builtin(builtin_names[typ.fullname], line) + elif builtin := builder.load_builtin(typ.fullname, line): + class_obj = builtin elif isinstance(unbounded_type, UnboundType): path_parts = unbounded_type.name.split(".") class_obj = builder.load_global_str(path_parts[0], line) @@ -1011,7 +1010,8 @@ def gen_native_func_call_and_return(fdef: FuncDef) -> None: coerced = builder.coerce(ret_val, current_func_decl.sig.ret_type, line) builder.add(Return(coerced)) - int_type_obj = builder.load_builtin(builtin_names["builtins.int"], line) + int_type_obj = builder.load_builtin("builtins.int", line) + assert int_type_obj is_int = builder.builder.type_is_op(impl_to_use, int_type_obj, line) native_call, non_native_call = BasicBlock(), BasicBlock() diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index c7c1c0a944343..c19eded77464e 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -183,10 +183,11 @@ ) from mypyc.primitives.registry import ( ERR_NEG_INT, - BuiltInName, CFunctionDescription, binary_ops, + builtin_names, function_ops, + global_names, method_call_ops, unary_ops, ) @@ -330,10 +331,12 @@ def load_address(self, name: str, rtype: RType, line: int = -1) -> Value: def load_global(self, name: str, rtype: RType, line: int) -> Value: return self.add(LoadGlobal(rtype, name, line)) - def load_builtin(self, builtin: BuiltInName, line: int) -> Value: - if builtin.is_ptr: - return self.load_global(builtin.src, builtin.type, line) - return self.load_address(builtin.src, builtin.type, line) + def load_builtin(self, name: str, line: int) -> Value | None: + if builtin := builtin_names.get(name): + return self.load_address(builtin[1], builtin[0], line) + if glob := global_names.get(name): + return self.load_global(glob[1], glob[0], line) + return None def load_struct_field( self, ptr: Value, struct: RStruct, field: str, *, borrow: bool = False diff --git a/mypyc/irbuild/vec.py b/mypyc/irbuild/vec.py index 19d607b197a0b..00e6f0adcdd8f 100644 --- a/mypyc/irbuild/vec.py +++ b/mypyc/irbuild/vec.py @@ -54,7 +54,6 @@ vec_api_by_item_type, vec_item_type_tags, ) -from mypyc.primitives.registry import builtin_names if TYPE_CHECKING: from mypyc.irbuild.ll_builder import LowLevelIRBuilder @@ -213,7 +212,7 @@ def vec_item_type_info( builder: LowLevelIRBuilder, typ: RType, line: int ) -> tuple[Value | None, bool, int]: if isinstance(typ, RPrimitive) and typ.is_refcounted: - return builder.load_builtin(builtin_names[typ.name], line), False, 0 + return builder.load_builtin(typ.name, line), False, 0 elif isinstance(typ, RInstance): return builder.load_native_type_object(typ.name), False, 0 elif typ in vec_item_type_tags: diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index aadae7b3a7d72..7b78b61f50e26 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -31,6 +31,7 @@ custom_primitive_op, function_op, load_address_op, + load_global_op, method_op, ) @@ -52,11 +53,8 @@ ) # Get the boxed StopAsyncIteration object -stop_async_iteration_op = load_address_op( - name="builtins.StopAsyncIteration", - type=object_rprimitive, - src="PyExc_StopAsyncIteration", - is_ptr=True, +stop_async_iteration_op = load_global_op( + name="builtins.StopAsyncIteration", type=object_rprimitive, src="PyExc_StopAsyncIteration" ) # id(obj) diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index cad629dad39f1..b45a8684f2084 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -85,14 +85,11 @@ class LoadAddressDescription(NamedTuple): # Primitive ops for unary ops unary_ops: dict[str, list[PrimitiveDescription]] = {} +# Mapping of type name to (type, C variable name). The C variables are PyObject. +builtin_names: dict[str, tuple[RType, str]] = {} -class BuiltInName(NamedTuple): - type: RType - src: str # name of the target to load - is_ptr: bool # whether the target is already a pointer (PyObject *) - - -builtin_names: dict[str, BuiltInName] = {} +# Mapping of type name to (type, C variable name). The C variables are PyObject *. +global_names: dict[str, tuple[RType, str]] = {} def method_op( @@ -388,11 +385,15 @@ def unary_op( return desc -def load_address_op( - name: str, type: RType, src: str, *, is_ptr: bool = False -) -> LoadAddressDescription: +def load_address_op(name: str, type: RType, src: str) -> LoadAddressDescription: assert name not in builtin_names, "already defined: %s" % name - builtin_names[name] = BuiltInName(type, src, is_ptr) + builtin_names[name] = (type, src) + return LoadAddressDescription(name, type, src) + + +def load_global_op(name: str, type: RType, src: str) -> LoadAddressDescription: + assert name not in global_names, "already defined: %s" % name + global_names[name] = (type, src) return LoadAddressDescription(name, type, src) From 568679ab79f8cc27ea778d24ac49283fa30a03b2 Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Tue, 5 May 2026 16:42:45 +0200 Subject: [PATCH 7/8] Improve comments --- mypyc/primitives/registry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index b45a8684f2084..c04b4ff65a757 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -85,10 +85,10 @@ class LoadAddressDescription(NamedTuple): # Primitive ops for unary ops unary_ops: dict[str, list[PrimitiveDescription]] = {} -# Mapping of type name to (type, C variable name). The C variables are PyObject. +# Mapping of type name to (type, C value variable name). builtin_names: dict[str, tuple[RType, str]] = {} -# Mapping of type name to (type, C variable name). The C variables are PyObject *. +# Mapping of type name to (type, C pointer variable name). global_names: dict[str, tuple[RType, str]] = {} From 9d7db42f76cb80799e7ea9d27737d9380f9932c1 Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Tue, 5 May 2026 17:18:26 +0200 Subject: [PATCH 8/8] Fix test --- mypyc/test-data/run-async.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test index e24271b94f098..2733a31f3af2a 100644 --- a/mypyc/test-data/run-async.test +++ b/mypyc/test-data/run-async.test @@ -1930,7 +1930,7 @@ async def test_wrapper() -> None: assert new_list == ['a', 'b', 'c', 'd'] with assertRaises(StopAsyncIteration): - await async_iter([]).__anext__() + await AsyncIter([]).__anext__() [file async_iter.py] from typing import AsyncIterator