Skip to content

Commit 5e83bd6

Browse files
authored
Fix handling of default arguments in generated glue methods (#10825)
Currently these mostly segfault. The main tricky case here is with *args and **kwargs (which we need to deal with always when generating pycall glue and also when a native function takes them), though we also need to take care to coerce error values when forwarding optional arguments. I also accidentally fixed a bug where we would compile `f(a, *b, c)` as `f(a, c, *b)`.
1 parent d180456 commit 5e83bd6

File tree

10 files changed

+500
-84
lines changed

10 files changed

+500
-84
lines changed

mypyc/ir/func_ir.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,27 +19,32 @@ class RuntimeArg:
1919
Argument kind is one of ARG_* constants defined in mypy.nodes.
2020
"""
2121

22-
def __init__(self, name: str, typ: RType, kind: ArgKind = ARG_POS) -> None:
22+
def __init__(
23+
self, name: str, typ: RType, kind: ArgKind = ARG_POS, pos_only: bool = False) -> None:
2324
self.name = name
2425
self.type = typ
2526
self.kind = kind
27+
self.pos_only = pos_only
2628

2729
@property
2830
def optional(self) -> bool:
2931
return self.kind.is_optional()
3032

3133
def __repr__(self) -> str:
32-
return 'RuntimeArg(name=%s, type=%s, optional=%r)' % (self.name, self.type, self.optional)
34+
return 'RuntimeArg(name=%s, type=%s, optional=%r, pos_only=%r)' % (
35+
self.name, self.type, self.optional, self.pos_only)
3336

3437
def serialize(self) -> JsonDict:
35-
return {'name': self.name, 'type': self.type.serialize(), 'kind': int(self.kind.value)}
38+
return {'name': self.name, 'type': self.type.serialize(), 'kind': int(self.kind.value),
39+
'pos_only': self.pos_only}
3640

3741
@classmethod
3842
def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> 'RuntimeArg':
3943
return RuntimeArg(
4044
data['name'],
4145
deserialize_type(data['type'], ctx),
4246
ArgKind(data['kind']),
47+
data['pos_only'],
4348
)
4449

4550

mypyc/irbuild/function.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from mypyc.primitives.dict_ops import dict_set_item_op
3535
from mypyc.common import SELF_NAME, LAMBDA_NAME, decorator_helper_name
3636
from mypyc.sametype import is_same_method_signature
37-
from mypyc.irbuild.util import concrete_arg_kind, is_constant
37+
from mypyc.irbuild.util import is_constant
3838
from mypyc.irbuild.context import FuncInfo, ImplicitClass
3939
from mypyc.irbuild.targets import AssignmentTarget
4040
from mypyc.irbuild.statement import transform_try_except
@@ -660,9 +660,10 @@ def get_args(builder: IRBuilder, rt_args: Sequence[RuntimeArg], line: int) -> Ar
660660
fake_vars = [(Var(arg.name), arg.type) for arg in rt_args]
661661
args = [builder.read(builder.add_local_reg(var, type, is_arg=True), line)
662662
for var, type in fake_vars]
663-
arg_names = [arg.name if arg.kind.is_named() else None
663+
arg_names = [arg.name
664+
if arg.kind.is_named() or (arg.kind.is_optional() and not arg.pos_only) else None
664665
for arg in rt_args]
665-
arg_kinds = [concrete_arg_kind(arg.kind) for arg in rt_args]
666+
arg_kinds = [arg.kind for arg in rt_args]
666667
return ArgInfo(args, arg_names, arg_kinds)
667668

668669

@@ -705,9 +706,24 @@ def f(builder: IRBuilder, x: object) -> int: ...
705706
arg_info = get_args(builder, rt_args, line)
706707
args, arg_kinds, arg_names = arg_info.args, arg_info.arg_kinds, arg_info.arg_names
707708

709+
# We can do a passthrough *args/**kwargs with a native call, but if the
710+
# args need to get distributed out to arguments, we just let python handle it
711+
if (
712+
any(kind.is_star() for kind in arg_kinds)
713+
and any(not arg.kind.is_star() for arg in target.decl.sig.args)
714+
):
715+
do_pycall = True
716+
708717
if do_pycall:
718+
if target.decl.kind == FUNC_STATICMETHOD:
719+
# FIXME: this won't work if we can do interpreted subclasses
720+
first = builder.builder.get_native_type(cls)
721+
st = 0
722+
else:
723+
first = args[0]
724+
st = 1
709725
retval = builder.builder.py_method_call(
710-
args[0], target.name, args[1:], line, arg_kinds[1:], arg_names[1:])
726+
first, target.name, args[st:], line, arg_kinds[st:], arg_names[st:])
711727
else:
712728
retval = builder.builder.call(target.decl, args, arg_kinds, arg_names, line)
713729
retval = builder.coerce(retval, sig.ret_type, line)

0 commit comments

Comments
 (0)