Skip to content

[mypyc] Fix *args and **kwargs in super() calls to native methods - #22191

Open
rheard wants to merge 1 commit into
python:masterfrom
rheard:fix-mypyc-1248
Open

rheard wants to merge 1 commit into
python:masterfrom
rheard:fix-mypyc-1248

Conversation

@rheard

@rheard rheard commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Fixes mypyc/mypyc#1248.
Fixes mypyc/mypyc#1240.
Fixes mypyc/mypyc#1162.

native_args_to_positional() can pass a *args / **kwargs argument on to a *args / **kwargs parameter, but it can't unpack one. Most native call sites check for this and use a Python-level call instead (call_refexpr_with_args(), gen_method_call(), gen_glue_method(), and Class.method(...) since #18228). translate_super_method_call() and the cls.method(...) case in translate_method_call() didn't.

The check is now in LowLevelIRBuilder.call(), which all of these go through. can_pass_args_natively() uses the same argument mapping as native_args_to_positional() and reports whether a *args / **kwargs argument is mapped to a parameter of another kind. If so, call() calls the method through its wrapper function, which parses arguments like a call from interpreted code. CPy_GetMethodWrapper() gets a callable for the wrapper from tp_methods of the class that defines the method (CPy_SetupObject() reads tp_methods in the same way).

The method table had METH_FASTCALL | METH_KEYWORDS for every method, but __init__ and __new__ have legacy wrappers that take a tuple and a dict, and the wrapper of __new__ takes the class as self. CPy_GetMethodWrapper() needs the real flags, so these two now have METH_VARARGS | METH_KEYWORDS, and __new__ has METH_CLASS. CPython doesn't use these entries when it sets up the type, since the slot wrappers get into the type dict first, and the type dict is unchanged. The exception is __new__ of a class with a built-in base class, which has no tp_new: there the entry is in the type dict, and calling it from interpreted code crashed before and raises TypeError now. That needs its own fix. is_fastcall_supported() moves from emitmodule.py to emitwrapper.py so that emitclass.py can use it.

Calls without *args / **kwargs, and calls that pass them on to *args / **kwargs of the method, are native calls as before. An unpacked argument that no parameter of the method can take is still ignored, as before.

The wrapper call is much slower than a native call, a bit faster than the same call in interpreted code. It only replaces calls that didn't work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant