diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index a312311b21a0..a9d7360cda20 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -683,8 +683,11 @@ def emit_attr_defaults_func_call(defaults_fn: FuncIR, self_name: str, emitter: E The code returns NULL on a raised exception. """ emitter.emit_lines( - "if ({}{}((PyObject *){}) == 0) {{".format( - NATIVE_PREFIX, defaults_fn.cname(emitter.names), self_name + "if ({}{}{}((PyObject *){}) == 0) {{".format( + emitter.get_group_prefix(defaults_fn.decl), + NATIVE_PREFIX, + defaults_fn.cname(emitter.names), + self_name, ), "Py_DECREF(self);", "return NULL;", diff --git a/mypyc/test-data/run-multimodule.test b/mypyc/test-data/run-multimodule.test index 3ab589ab1530..aa9dd7eb5ef1 100644 --- a/mypyc/test-data/run-multimodule.test +++ b/mypyc/test-data/run-multimodule.test @@ -1423,6 +1423,45 @@ class Parent: from native import test test() +[case testIncrementalCrossModuleInheritedAttrDefaults] +# Regression: under separate=True, when only the subclass module is +# recompiled (parent loaded from mypy's incremental cache, so its +# ClassDef.defs.body is empty), the subclass produces no +# __mypyc_defaults_setup of its own and ClassIR.get_method returns +# the parent's. The emitted call must use the cross-group +# exports_. prefix, otherwise the generated C references an +# undeclared symbol and clang/gcc fail to compile. +import other_a + +def test() -> None: + c = other_a.Child() + assert c.x == 1 + assert c.y == "hello" + +[file other_b.py] +class Parent: + x: int = 1 + y: str = "hello" + +[file other_a.py] +from other_b import Parent + +class Child(Parent): + pass + +[file other_a.py.2] +from other_b import Parent + +class Child(Parent): + pass + +def _force_recompile() -> int: + return 1 + +[file driver.py] +from native import test +test() + [case testExtraOpsFunction_experimental_librt] from librt.strings import BytesWriter