diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index 7d41ee7e162b..5894f7b7ed3f 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -759,10 +759,12 @@ def emit_union_cast( self.emit_line(f"PyObject *{dest};") good_label = self.new_label() if optional: - self.emit_line(f"if ({src} == NULL) {{") - self.emit_line(f"{dest} = {self.c_error_value(typ)};") - self.emit_line(f"goto {good_label};") - self.emit_line("}") + self.emit_lines( + f"if ({src} == NULL) {{", + f"{dest} = {self.c_error_value(typ)};", + f"goto {good_label};", + "}", + ) for item in typ.items: self.emit_cast( src, @@ -1030,9 +1032,11 @@ def emit_box( self.emit_line(f"{declaration}{dest} = PyFloat_FromDouble({src});") elif isinstance(typ, RTuple): self.declare_tuple_struct(typ) - self.emit_line(f"{declaration}{dest} = PyTuple_New({len(typ.types)});") - self.emit_line(f"if (unlikely({dest} == NULL))") - self.emit_line(" CPyError_OutOfMemory();") + self.emit_lines( + f"{declaration}{dest} = PyTuple_New({len(typ.types)});", + f"if (unlikely({dest} == NULL))", + " CPyError_OutOfMemory();", + ) # TODO: Fail if dest is None for i in range(0, len(typ.types)): if not typ.is_unboxed: @@ -1072,9 +1076,11 @@ def emit_gc_visit(self, target: str, rtype: RType) -> None: # Not refcounted -> no pointers -> no GC interaction. return elif isinstance(rtype, RPrimitive) and rtype.name == "builtins.int": - self.emit_line(f"if (CPyTagged_CheckLong({target})) {{") - self.emit_line(f"Py_VISIT(CPyTagged_LongAsObject({target}));") - self.emit_line("}") + self.emit_lines( + f"if (CPyTagged_CheckLong({target})) {{", + f"Py_VISIT(CPyTagged_LongAsObject({target}));", + "}", + ) elif isinstance(rtype, RTuple): for i, item_type in enumerate(rtype.types): self.emit_gc_visit(f"{target}.f{i}", item_type) @@ -1094,11 +1100,13 @@ def emit_gc_clear(self, target: str, rtype: RType) -> None: # Not refcounted -> no pointers -> no GC interaction. return elif isinstance(rtype, RPrimitive) and rtype.name == "builtins.int": - self.emit_line(f"if (CPyTagged_CheckLong({target})) {{") - self.emit_line(f"CPyTagged __tmp = {target};") - self.emit_line(f"{target} = {self.c_undefined_value(rtype)};") - self.emit_line("Py_XDECREF(CPyTagged_LongAsObject(__tmp));") - self.emit_line("}") + self.emit_lines( + f"if (CPyTagged_CheckLong({target})) {{", + f"CPyTagged __tmp = {target};", + f"{target} = {self.c_undefined_value(rtype)};", + "Py_XDECREF(CPyTagged_LongAsObject(__tmp));", + "}", + ) elif isinstance(rtype, RTuple): for i, item_type in enumerate(rtype.types): self.emit_gc_clear(f"{target}.f{i}", item_type) @@ -1156,9 +1164,9 @@ def _emit_traceback( def emit_unbox_failure_with_overlapping_error_value( self, dest: str, typ: RType, failure: str ) -> None: - self.emit_line(f"if ({dest} == {self.c_error_value(typ)} && PyErr_Occurred()) {{") - self.emit_line(failure) - self.emit_line("}") + self.emit_lines( + f"if ({dest} == {self.c_error_value(typ)} && PyErr_Occurred()) {{", failure, "}" + ) def c_array_initializer(components: list[str], *, indented: bool = False) -> str: diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 84d19d69d377..0c59f8fd349c 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -472,9 +472,7 @@ def trait_offset_table_name(trait: ClassIR) -> str: ) # Emit vtable setup function - emitter.emit_line("static bool") - emitter.emit_line(f"{NATIVE_PREFIX}{vtable_setup_name}(void)") - emitter.emit_line("{") + emitter.emit_lines("static bool", f"{NATIVE_PREFIX}{vtable_setup_name}(void)", "{") if base.allow_interpreted_subclasses and not shadow: emitter.emit_line(f"{NATIVE_PREFIX}{vtable_setup_name}_shadow();") @@ -489,8 +487,7 @@ def trait_offset_table_name(trait: ClassIR) -> str: generate_vtable(base.vtable_entries, vtable_name, emitter, subtables, shadow) - emitter.emit_line("return 1;") - emitter.emit_line("}") + emitter.emit_lines("return 1;", "}") return vtable_name if not subtables else f"{vtable_name} + {len(subtables) * 3}" @@ -555,20 +552,24 @@ def generate_setup_for_class( emitter: Emitter, ) -> None: """Generate a native function that allocates an instance of a class.""" - emitter.emit_line("static PyObject *") - emitter.emit_line(f"{func_name}(PyTypeObject *type)") - emitter.emit_line("{") - emitter.emit_line(f"{cl.struct_name(emitter.names)} *self;") - emitter.emit_line(f"self = ({cl.struct_name(emitter.names)} *)type->tp_alloc(type, 0);") - emitter.emit_line("if (self == NULL)") - emitter.emit_line(" return NULL;") + emitter.emit_lines( + "static PyObject *", + f"{func_name}(PyTypeObject *type)", + "{", + f"{cl.struct_name(emitter.names)} *self;", + f"self = ({cl.struct_name(emitter.names)} *)type->tp_alloc(type, 0);", + "if (self == NULL)", + " return NULL;", + ) if shadow_vtable_name: - emitter.emit_line(f"if (type != {emitter.type_struct_name(cl)}) {{") - emitter.emit_line(f"self->vtable = {shadow_vtable_name};") - emitter.emit_line("} else {") - emitter.emit_line(f"self->vtable = {vtable_name};") - emitter.emit_line("}") + emitter.emit_lines( + f"if (type != {emitter.type_struct_name(cl)}) {{", + f"self->vtable = {shadow_vtable_name};", + "} else {", + f"self->vtable = {vtable_name};", + "}", + ) else: emitter.emit_line(f"self->vtable = {vtable_name};") for i in range(0, len(cl.bitmap_attrs), BITMAP_BITS): @@ -599,8 +600,7 @@ def generate_setup_for_class( "}", ) - emitter.emit_line("return (PyObject *)self;") - emitter.emit_line("}") + emitter.emit_lines("return (PyObject *)self;", "}") def generate_constructor_for_class( @@ -612,37 +612,39 @@ def generate_constructor_for_class( emitter: Emitter, ) -> None: """Generate a native function that allocates and initializes an instance of a class.""" - emitter.emit_line(f"{native_function_header(fn, emitter)}") - emitter.emit_line("{") - emitter.emit_line(f"PyObject *self = {setup_name}({emitter.type_struct_name(cl)});") - emitter.emit_line("if (self == NULL)") - emitter.emit_line(" return NULL;") + emitter.emit_lines( + f"{native_function_header(fn, emitter)}", + "{", + f"PyObject *self = {setup_name}({emitter.type_struct_name(cl)});", + "if (self == NULL)", + " return NULL;", + ) args = ", ".join(["self"] + [REG_PREFIX + arg.name for arg in fn.sig.args]) if init_fn is not None: - emitter.emit_line( + emitter.emit_lines( "char res = {}{}{}({});".format( emitter.get_group_prefix(init_fn.decl), NATIVE_PREFIX, init_fn.cname(emitter.names), args, - ) + ), + "if (res == 2) {", + "Py_DECREF(self);", + "return NULL;", + "}", ) - emitter.emit_line("if (res == 2) {") - emitter.emit_line("Py_DECREF(self);") - emitter.emit_line("return NULL;") - emitter.emit_line("}") # If there is a nontrivial ctor that we didn't define, invoke it via tp_init elif len(fn.sig.args) > 1: - emitter.emit_line(f"int res = {emitter.type_struct_name(cl)}->tp_init({args});") - - emitter.emit_line("if (res < 0) {") - emitter.emit_line("Py_DECREF(self);") - emitter.emit_line("return NULL;") - emitter.emit_line("}") + emitter.emit_lines( + f"int res = {emitter.type_struct_name(cl)}->tp_init({args});", + "if (res < 0) {", + "Py_DECREF(self);", + "return NULL;", + "}", + ) - emitter.emit_line("return self;") - emitter.emit_line("}") + emitter.emit_lines("return self;", "}") def generate_init_for_class(cl: ClassIR, init_fn: FuncIR, emitter: Emitter) -> str: @@ -654,9 +656,9 @@ def generate_init_for_class(cl: ClassIR, init_fn: FuncIR, emitter: Emitter) -> s """ func_name = f"{cl.name_prefix(emitter.names)}_init" - emitter.emit_line("static int") - emitter.emit_line(f"{func_name}(PyObject *self, PyObject *args, PyObject *kwds)") - emitter.emit_line("{") + emitter.emit_lines( + "static int", f"{func_name}(PyObject *self, PyObject *args, PyObject *kwds)", "{" + ) if cl.allow_interpreted_subclasses or cl.builtin_base: emitter.emit_line( "return {}{}(self, args, kwds) != NULL ? 0 : -1;".format( @@ -678,17 +680,19 @@ def generate_new_for_class( init_fn: FuncIR | None, emitter: Emitter, ) -> None: - emitter.emit_line("static PyObject *") - emitter.emit_line(f"{func_name}(PyTypeObject *type, PyObject *args, PyObject *kwds)") - emitter.emit_line("{") + emitter.emit_lines( + "static PyObject *", + f"{func_name}(PyTypeObject *type, PyObject *args, PyObject *kwds)", + "{", + ) # TODO: Check and unbox arguments if not cl.allow_interpreted_subclasses: - emitter.emit_line(f"if (type != {emitter.type_struct_name(cl)}) {{") - emitter.emit_line( - 'PyErr_SetString(PyExc_TypeError, "interpreted classes cannot inherit from compiled");' + emitter.emit_lines( + f"if (type != {emitter.type_struct_name(cl)}) {{", + 'PyErr_SetString(PyExc_TypeError, "interpreted classes cannot inherit from compiled");', + "return NULL;", + "}", ) - emitter.emit_line("return NULL;") - emitter.emit_line("}") if not init_fn or cl.allow_interpreted_subclasses or cl.builtin_base or cl.is_serializable(): # Match Python semantics -- __new__ doesn't call __init__. @@ -697,30 +701,32 @@ def generate_new_for_class( # __new__ of a native class implicitly calls __init__ so that we # can enforce that instances are always properly initialized. This # is needed to support always defined attributes. - emitter.emit_line(f"PyObject *self = {setup_name}(type);") - emitter.emit_lines("if (self == NULL)", " return NULL;") - emitter.emit_line( - f"PyObject *ret = {PREFIX}{init_fn.cname(emitter.names)}(self, args, kwds);" + emitter.emit_lines( + f"PyObject *self = {setup_name}(type);", + "if (self == NULL)", + " return NULL;", + f"PyObject *ret = {PREFIX}{init_fn.cname(emitter.names)}(self, args, kwds);", + "if (ret == NULL)", + " return NULL;", + "return self;", ) - emitter.emit_lines("if (ret == NULL)", " return NULL;") - emitter.emit_line("return self;") emitter.emit_line("}") def generate_new_for_trait(cl: ClassIR, func_name: str, emitter: Emitter) -> None: - emitter.emit_line("static PyObject *") - emitter.emit_line(f"{func_name}(PyTypeObject *type, PyObject *args, PyObject *kwds)") - emitter.emit_line("{") - emitter.emit_line(f"if (type != {emitter.type_struct_name(cl)}) {{") - emitter.emit_line( + emitter.emit_lines( + "static PyObject *", + f"{func_name}(PyTypeObject *type, PyObject *args, PyObject *kwds)", + "{", + f"if (type != {emitter.type_struct_name(cl)}) {{", "PyErr_SetString(PyExc_TypeError, " - '"interpreted classes cannot inherit from compiled traits");' + '"interpreted classes cannot inherit from compiled traits");', + "} else {", + 'PyErr_SetString(PyExc_TypeError, "traits may not be directly created");', + "}", + "return NULL;", + "}", ) - emitter.emit_line("} else {") - emitter.emit_line('PyErr_SetString(PyExc_TypeError, "traits may not be directly created");') - emitter.emit_line("}") - emitter.emit_line("return NULL;") - emitter.emit_line("}") def generate_traverse_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -> None: @@ -775,16 +781,18 @@ def generate_clear_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -> N def generate_dealloc_for_class( cl: ClassIR, dealloc_func_name: str, clear_func_name: str, emitter: Emitter ) -> None: - emitter.emit_line("static void") - emitter.emit_line(f"{dealloc_func_name}({cl.struct_name(emitter.names)} *self)") - emitter.emit_line("{") - emitter.emit_line("PyObject_GC_UnTrack(self);") - # The trashcan is needed to handle deep recursive deallocations - emitter.emit_line(f"CPy_TRASHCAN_BEGIN(self, {dealloc_func_name})") - emitter.emit_line(f"{clear_func_name}(self);") - emitter.emit_line("Py_TYPE(self)->tp_free((PyObject *)self);") - emitter.emit_line("CPy_TRASHCAN_END(self)") - emitter.emit_line("}") + emitter.emit_lines( + "static void", + f"{dealloc_func_name}({cl.struct_name(emitter.names)} *self)", + "{", + "PyObject_GC_UnTrack(self);", + # The trashcan is needed to handle deep recursive deallocations + f"CPy_TRASHCAN_BEGIN(self, {dealloc_func_name})", + f"{clear_func_name}(self);", + "Py_TYPE(self)->tp_free((PyObject *)self);", + "CPy_TRASHCAN_END(self)", + "}", + ) def generate_methods_table(cl: ClassIR, name: str, emitter: Emitter) -> None: @@ -933,10 +941,12 @@ def generate_getter(cl: ClassIR, attr: str, rtype: RType, emitter: Emitter) -> N if not always_defined: emitter.emit_undefined_attr_check(rtype, attr_expr, "==", "self", attr, cl, unlikely=True) - emitter.emit_line("PyErr_SetString(PyExc_AttributeError,") - emitter.emit_line(f' "attribute {repr(attr)} of {repr(cl.name)} undefined");') - emitter.emit_line("return NULL;") - emitter.emit_line("}") + emitter.emit_lines( + "PyErr_SetString(PyExc_AttributeError,", + f' "attribute {repr(attr)} of {repr(cl.name)} undefined");', + "return NULL;", + "}", + ) emitter.emit_inc_ref(f"self->{attr_field}", rtype) emitter.emit_box(f"self->{attr_field}", "retval", rtype, declare_dest=True) emitter.emit_line("return retval;") @@ -955,13 +965,13 @@ def generate_setter(cl: ClassIR, attr: str, rtype: RType, emitter: Emitter) -> N deletable = cl.is_deletable(attr) if not deletable: - emitter.emit_line("if (value == NULL) {") - emitter.emit_line("PyErr_SetString(PyExc_AttributeError,") - emitter.emit_line( - f' "{repr(cl.name)} object attribute {repr(attr)} cannot be deleted");' + emitter.emit_lines( + "if (value == NULL) {", + "PyErr_SetString(PyExc_AttributeError,", + f' "{repr(cl.name)} object attribute {repr(attr)} cannot be deleted");', + "return -1;", + "}", ) - emitter.emit_line("return -1;") - emitter.emit_line("}") # HACK: Don't consider refcounted values as always defined, since it's possible to # access uninitialized values via 'gc.get_objects()'. Accessing non-refcounted diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index f360fabbe8f6..55b9d0f282ec 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -567,20 +567,24 @@ def generate_c_for_modules(self) -> list[tuple[str, str]]: # (which are shared between shared libraries via dynamic # exports tables and not accessed directly.) ext_declarations = Emitter(self.context) - ext_declarations.emit_line(f"#ifndef MYPYC_NATIVE{self.group_suffix}_H") - ext_declarations.emit_line(f"#define MYPYC_NATIVE{self.group_suffix}_H") - ext_declarations.emit_line("#include ") - ext_declarations.emit_line("#include ") + ext_declarations.emit_lines( + f"#ifndef MYPYC_NATIVE{self.group_suffix}_H", + f"#define MYPYC_NATIVE{self.group_suffix}_H", + "#include ", + "#include ", + ) declarations = Emitter(self.context) - declarations.emit_line(f"#ifndef MYPYC_NATIVE_INTERNAL{self.group_suffix}_H") - declarations.emit_line(f"#define MYPYC_NATIVE_INTERNAL{self.group_suffix}_H") - declarations.emit_line("#include ") - declarations.emit_line("#include ") - declarations.emit_line(f'#include "__native{self.short_group_suffix}.h"') - declarations.emit_line() - declarations.emit_line("int CPyGlobalsInit(void);") - declarations.emit_line() + declarations.emit_lines( + f"#ifndef MYPYC_NATIVE_INTERNAL{self.group_suffix}_H", + f"#define MYPYC_NATIVE_INTERNAL{self.group_suffix}_H", + "#include ", + "#include ", + f'#include "__native{self.short_group_suffix}.h"', + "", + "int CPyGlobalsInit(void);", + "", + ) for module_name, module in self.modules.items(): self.declare_finals(module_name, module.final_names, declarations) @@ -868,9 +872,7 @@ def generate_module_def(self, emitter: Emitter, module_name: str, module: Module "NULL /* docstring */}}," ).format(name=name, cname=fn.cname(emitter.names), prefix=PREFIX, flag=flag) ) - emitter.emit_line("{NULL, NULL, 0, NULL}") - emitter.emit_line("};") - emitter.emit_line() + emitter.emit_lines("{NULL, NULL, 0, NULL}", "};", "") # Emit module definition struct emitter.emit_lines( diff --git a/mypyc/test/test_emit.py b/mypyc/test/test_emit.py index e4ace3ec01f0..9c741184c2f4 100644 --- a/mypyc/test/test_emit.py +++ b/mypyc/test/test_emit.py @@ -50,6 +50,11 @@ def test_emit_line(self) -> None: 21, 22, 23, 24, 25, 26, 27, 28, 29] */\n""" ) + def test_emit_lines(self) -> None: + emitter = Emitter(self.context, {}) + emitter.emit_lines("line;", "a {", "f();", "}") + assert emitter.fragments == ["line;\n", "a {\n", " f();\n", "}\n"] + def test_emit_undefined_value_for_simple_type(self) -> None: emitter = Emitter(self.context, {}) assert emitter.c_undefined_value(int_rprimitive) == "CPY_INT_TAG"