diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index 523c718c9b86..b0e535dd8757 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -181,9 +181,15 @@ def get_arg_rtype(self, typ: Type, kind: ArgKind) -> RType: def fdef_to_sig(self, fdef: FuncDef, strict_dunders_typing: bool) -> FuncSignature: if isinstance(fdef.type, CallableType): + # Use the parameter variable's type rather than the declared Callable arg + # type: under --allow-redefinition-new the variable type is widened to the + # join of the declaration and every redefinition, and the local register + # (and any closure environment attribute) is created with that wider type. + # Keeping the declared type here would make the native signature narrower + # than the register that actually receives the argument (#22221). arg_types = [ - self.get_arg_rtype(typ, kind) - for typ, kind in zip(fdef.type.arg_types, fdef.type.arg_kinds) + self.get_arg_rtype(arg.variable.type or typ, kind) + for arg, typ, kind in zip(fdef.arguments, fdef.type.arg_types, fdef.type.arg_kinds) ] arg_pos_onlys = [name is None for name in fdef.type.arg_names] ret = self.type_to_rtype(fdef.type.ret_type) diff --git a/mypyc/test/test_irbuild.py b/mypyc/test/test_irbuild.py index 0dc19b62a60f..f91ded9a675f 100644 --- a/mypyc/test/test_irbuild.py +++ b/mypyc/test/test_irbuild.py @@ -4,8 +4,10 @@ import os.path import sys +import unittest from mypy.errors import CompileError +from mypy.nodes import FuncDef from mypy.test.config import test_temp_dir from mypy.test.data import DataDrivenTestCase from mypyc.common import TOP_LEVEL_NAME @@ -15,6 +17,7 @@ MypycDataSuite, assert_test_output, build_ir_for_single_file, + build_ir_for_single_file2, infer_ir_build_options_from_test_name, remove_comment_lines, replace_word_size, @@ -104,3 +107,30 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: actual.extend(format_func(fn)) assert_test_output(testcase, actual, "Invalid source code output", expected_output) + + +class TestRedefinedParamSignature(unittest.TestCase): + def test_widens_native_signature(self) -> None: + """The native signature must use the (possibly widened) parameter variable type. + + Under --allow-redefinition the variable's type is the join of the + declaration and every redefinition. The local argument register and any + closure environment attribute use that wider type, so a narrower + signature derived from the declared Callable type generates invalid C + when the argument is stored (https://github.com/python/mypy/issues/22221). + """ + source = [ + "# flags: --allow-redefinition", + "def f(n: int):", + " def inner():", + " nonlocal n", + " n = n - 1", + " return n", + " return inner()", + ] + module, tree, _, mapper = build_ir_for_single_file2(source) + fdef = tree.defs[0] + assert isinstance(fdef, FuncDef) + sig = mapper.func_to_decl[fdef].sig + assert sig.args[0].name == "n" + assert str(sig.args[0].type) == "union[int, object]"