Skip to content

Commit 793cf18

Browse files
authored
[mypyc] Support var arg in CallC, replace new_dict_op (python#8948)
Related: mypyc/mypyc#709, mypyc/mypyc#734 Summary: * introduce variable arguments in CallC * replace old new_dict_op (which relies on a specialized emit callback) with two CallC ops: dict_new_op and dict_build_op, which handles create an empty dict and a dict from k-v pairs. * fix related IR tests, especially separate the testDel case into three subcases which test list, dict and attributes respectively.
1 parent 3b2e114 commit 793cf18

File tree

11 files changed

+323
-210
lines changed

11 files changed

+323
-210
lines changed

mypyc/ir/ops.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1151,13 +1151,15 @@ def __init__(self,
11511151
ret_type: RType,
11521152
steals: StealsDescription,
11531153
error_kind: int,
1154-
line: int) -> None:
1154+
line: int,
1155+
var_arg_idx: int = -1) -> None:
11551156
self.error_kind = error_kind
11561157
super().__init__(line)
11571158
self.function_name = function_name
11581159
self.args = args
11591160
self.type = ret_type
11601161
self.steals = steals
1162+
self.var_arg_idx = var_arg_idx # the position of the first variable argument in args
11611163

11621164
def to_str(self, env: Environment) -> str:
11631165
args_str = ', '.join(env.format('%r', arg) for arg in self.args)

mypyc/irbuild/classdef.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
dataclass_sleight_of_hand, pytype_from_template_op, py_calc_meta_op, type_object_op,
2222
not_implemented_op, true_op
2323
)
24-
from mypyc.primitives.dict_ops import dict_set_item_op, new_dict_op
24+
from mypyc.primitives.dict_ops import dict_set_item_op, dict_new_op
2525
from mypyc.primitives.tuple_ops import new_tuple_op
2626
from mypyc.common import SELF_NAME
2727
from mypyc.irbuild.util import (
@@ -73,7 +73,7 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None:
7373
# We populate __annotations__ for non-extension classes
7474
# because dataclasses uses it to determine which attributes to compute on.
7575
# TODO: Maybe generate more precise types for annotations
76-
non_ext_anns = builder.primitive_op(new_dict_op, [], cdef.line)
76+
non_ext_anns = builder.call_c(dict_new_op, [], cdef.line)
7777
non_ext = NonExtClassInfo(non_ext_dict, non_ext_bases, non_ext_anns, non_ext_metaclass)
7878
dataclass_non_ext = None
7979
type_obj = None
@@ -258,7 +258,7 @@ def setup_non_ext_dict(builder: IRBuilder,
258258
builder.goto(exit_block)
259259

260260
builder.activate_block(false_block)
261-
builder.assign(non_ext_dict, builder.primitive_op(new_dict_op, [], cdef.line), cdef.line)
261+
builder.assign(non_ext_dict, builder.call_c(dict_new_op, [], cdef.line), cdef.line)
262262
builder.goto(exit_block)
263263
builder.activate_block(exit_block)
264264

@@ -518,9 +518,9 @@ def dataclass_non_ext_info(builder: IRBuilder, cdef: ClassDef) -> Optional[NonEx
518518
"""
519519
if is_dataclass(cdef):
520520
return NonExtClassInfo(
521-
builder.primitive_op(new_dict_op, [], cdef.line),
521+
builder.call_c(dict_new_op, [], cdef.line),
522522
builder.add(TupleSet([], cdef.line)),
523-
builder.primitive_op(new_dict_op, [], cdef.line),
523+
builder.call_c(dict_new_op, [], cdef.line),
524524
builder.primitive_op(type_object_op, [], cdef.line),
525525
)
526526
else:

mypyc/irbuild/expression.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from mypyc.primitives.misc_ops import new_slice_op, ellipsis_op, type_op
2626
from mypyc.primitives.list_ops import new_list_op, list_append_op, list_extend_op
2727
from mypyc.primitives.tuple_ops import list_tuple_op
28-
from mypyc.primitives.dict_ops import new_dict_op, dict_set_item_op
28+
from mypyc.primitives.dict_ops import dict_new_op, dict_set_item_op
2929
from mypyc.primitives.set_ops import new_set_op, set_add_op, set_update_op
3030
from mypyc.irbuild.specialize import specializers
3131
from mypyc.irbuild.builder import IRBuilder
@@ -541,7 +541,7 @@ def gen_inner_stmts() -> None:
541541

542542

543543
def transform_dictionary_comprehension(builder: IRBuilder, o: DictionaryComprehension) -> Value:
544-
d = builder.primitive_op(new_dict_op, [], o.line)
544+
d = builder.call_c(dict_new_op, [], o.line)
545545
loop_params = list(zip(o.indices, o.sequences, o.condlists))
546546

547547
def gen_inner_stmts() -> None:

mypyc/irbuild/ll_builder.py

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from mypyc.ir.rtypes import (
2727
RType, RUnion, RInstance, optional_value_type, int_rprimitive, float_rprimitive,
2828
bool_rprimitive, list_rprimitive, str_rprimitive, is_none_rprimitive, object_rprimitive,
29+
c_int_rprimitive
2930
)
3031
from mypyc.ir.func_ir import FuncDecl, FuncSignature
3132
from mypyc.ir.class_ir import ClassIR, all_concrete_classes
@@ -41,7 +42,9 @@
4142
list_extend_op, list_len_op, new_list_op
4243
)
4344
from mypyc.primitives.tuple_ops import list_tuple_op, new_tuple_op
44-
from mypyc.primitives.dict_ops import new_dict_op, dict_update_in_display_op
45+
from mypyc.primitives.dict_ops import (
46+
dict_update_in_display_op, dict_new_op, dict_build_op
47+
)
4548
from mypyc.primitives.generic_ops import (
4649
py_getattr_op, py_call_op, py_call_with_kwargs_op, py_method_call_op
4750
)
@@ -566,12 +569,14 @@ def unary_op(self,
566569

567570
def make_dict(self, key_value_pairs: Sequence[DictEntry], line: int) -> Value:
568571
result = None # type: Union[Value, None]
569-
initial_items = [] # type: List[Value]
572+
keys = [] # type: List[Value]
573+
values = [] # type: List[Value]
570574
for key, value in key_value_pairs:
571575
if key is not None:
572576
# key:value
573577
if result is None:
574-
initial_items.extend((key, value))
578+
keys.append(key)
579+
values.append(value)
575580
continue
576581

577582
self.translate_special_method_call(
@@ -583,7 +588,7 @@ def make_dict(self, key_value_pairs: Sequence[DictEntry], line: int) -> Value:
583588
else:
584589
# **value
585590
if result is None:
586-
result = self.primitive_op(new_dict_op, initial_items, line)
591+
result = self._create_dict(keys, values, line)
587592

588593
self.primitive_op(
589594
dict_update_in_display_op,
@@ -592,7 +597,7 @@ def make_dict(self, key_value_pairs: Sequence[DictEntry], line: int) -> Value:
592597
)
593598

594599
if result is None:
595-
result = self.primitive_op(new_dict_op, initial_items, line)
600+
result = self._create_dict(keys, values, line)
596601

597602
return result
598603

@@ -685,12 +690,22 @@ def call_c(self,
685690
result_type: Optional[RType] = None) -> Value:
686691
# handle void function via singleton RVoid instance
687692
coerced = []
688-
for i, arg in enumerate(args):
693+
# coerce fixed number arguments
694+
for i in range(min(len(args), len(desc.arg_types))):
689695
formal_type = desc.arg_types[i]
696+
arg = args[i]
690697
arg = self.coerce(arg, formal_type, line)
691698
coerced.append(arg)
699+
# coerce any var_arg
700+
var_arg_idx = -1
701+
if desc.var_arg_type is not None:
702+
var_arg_idx = len(desc.arg_types)
703+
for i in range(len(desc.arg_types), len(args)):
704+
arg = args[i]
705+
arg = self.coerce(arg, desc.var_arg_type, line)
706+
coerced.append(arg)
692707
target = self.add(CallC(desc.c_function_name, coerced, desc.return_type, desc.steals,
693-
desc.error_kind, line))
708+
desc.error_kind, line, var_arg_idx))
694709
if result_type and not is_runtime_subtype(target.type, result_type):
695710
if is_none_rprimitive(result_type):
696711
# Special case None return. The actual result may actually be a bool
@@ -859,3 +874,18 @@ def translate_eq_cmp(self,
859874
ltype,
860875
line
861876
)
877+
878+
def _create_dict(self,
879+
keys: List[Value],
880+
values: List[Value],
881+
line: int) -> Value:
882+
"""Create a dictionary(possibly empty) using keys and values"""
883+
# keys and values should have the same number of items
884+
size = len(keys)
885+
if size > 0:
886+
load_size_op = self.add(LoadInt(size, -1, c_int_rprimitive))
887+
# merge keys and values
888+
items = [i for t in list(zip(keys, values)) for i in t]
889+
return self.call_c(dict_build_op, [load_size_op] + items, line)
890+
else:
891+
return self.call_c(dict_new_op, [], line)

mypyc/primitives/dict_ops.py

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
from mypyc.ir.ops import EmitterInterface, ERR_FALSE, ERR_MAGIC, ERR_NEVER
66
from mypyc.ir.rtypes import (
77
dict_rprimitive, object_rprimitive, bool_rprimitive, int_rprimitive,
8-
list_rprimitive, dict_next_rtuple_single, dict_next_rtuple_pair
8+
list_rprimitive, dict_next_rtuple_single, dict_next_rtuple_pair, c_int_rprimitive
99
)
1010

1111
from mypyc.primitives.registry import (
1212
name_ref_op, method_op, binary_op, func_op, custom_op,
1313
simple_emit, negative_int_emit, call_emit, call_negative_bool_emit,
14-
name_emit,
14+
name_emit, c_custom_op
1515
)
1616

1717

@@ -88,25 +88,22 @@
8888
error_kind=ERR_MAGIC,
8989
emit=simple_emit('{dest} = CPyDict_Get({args[0]}, {args[1]}, Py_None);'))
9090

91-
92-
def emit_new_dict(emitter: EmitterInterface, args: List[str], dest: str) -> None:
93-
if not args:
94-
emitter.emit_line('%s = PyDict_New();' % (dest,))
95-
return
96-
97-
emitter.emit_line('%s = CPyDict_Build(%s, %s);' % (dest, len(args) // 2, ', '.join(args)))
98-
91+
# Construct an empty dictionary.
92+
dict_new_op = c_custom_op(
93+
arg_types=[],
94+
return_type=dict_rprimitive,
95+
c_function_name='PyDict_New',
96+
error_kind=ERR_MAGIC)
9997

10098
# Construct a dictionary from keys and values.
101-
# Arguments are (key1, value1, ..., keyN, valueN).
102-
new_dict_op = custom_op(
103-
name='builtins.dict',
104-
arg_types=[object_rprimitive],
105-
is_var_arg=True,
106-
result_type=dict_rprimitive,
107-
format_str='{dest} = {{{colon_args}}}',
99+
# Positional argument is the number of key-value pairs
100+
# Variable arguments are (key1, value1, ..., keyN, valueN).
101+
dict_build_op = c_custom_op(
102+
arg_types=[c_int_rprimitive],
103+
return_type=dict_rprimitive,
104+
c_function_name='CPyDict_Build',
108105
error_kind=ERR_MAGIC,
109-
emit=emit_new_dict)
106+
var_arg_type=object_rprimitive,)
110107

111108
# Construct a dictionary from another dictionary.
112109
func_op(

mypyc/primitives/registry.py

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
'CFunctionDescription', [('name', str),
4747
('arg_types', List[RType]),
4848
('return_type', RType),
49+
('var_arg_type', Optional[RType]),
4950
('c_function_name', str),
5051
('error_kind', int),
5152
('steals', StealsDescription),
@@ -337,10 +338,25 @@ def c_method_op(name: str,
337338
return_type: RType,
338339
c_function_name: str,
339340
error_kind: int,
341+
var_arg_type: Optional[RType] = None,
340342
steals: StealsDescription = False,
341343
priority: int = 1) -> CFunctionDescription:
344+
"""Define a c function call op that replaces a method call.
345+
346+
This will be automatically generated by matching against the AST.
347+
348+
Args:
349+
name: short name of the method (for example, 'append')
350+
arg_types: argument types; the receiver is always the first argument
351+
return_type: type of the return value. Use void_rtype to represent void.
352+
c_function_name: name of the C function to call
353+
error_kind: how errors are represented in the result (one of ERR_*)
354+
var_arg_type: type of all variable arguments
355+
steals: description of arguments that this steals (ref count wise)
356+
priority: if multiple ops match, the one with the highest priority is picked
357+
"""
342358
ops = c_method_call_ops.setdefault(name, [])
343-
desc = CFunctionDescription(name, arg_types, return_type,
359+
desc = CFunctionDescription(name, arg_types, return_type, var_arg_type,
344360
c_function_name, error_kind, steals, priority)
345361
ops.append(desc)
346362
return desc
@@ -351,10 +367,21 @@ def c_function_op(name: str,
351367
return_type: RType,
352368
c_function_name: str,
353369
error_kind: int,
370+
var_arg_type: Optional[RType] = None,
354371
steals: StealsDescription = False,
355372
priority: int = 1) -> CFunctionDescription:
373+
"""Define a c function call op that replaces a function call.
374+
375+
This will be automatically generated by matching against the AST.
376+
377+
Most arguments are similar to c_method_op().
378+
379+
Args:
380+
name: full name of the function
381+
arg_types: positional argument types for which this applies
382+
"""
356383
ops = c_function_ops.setdefault(name, [])
357-
desc = CFunctionDescription(name, arg_types, return_type,
384+
desc = CFunctionDescription(name, arg_types, return_type, var_arg_type,
358385
c_function_name, error_kind, steals, priority)
359386
ops.append(desc)
360387
return desc
@@ -365,28 +392,58 @@ def c_binary_op(name: str,
365392
return_type: RType,
366393
c_function_name: str,
367394
error_kind: int,
395+
var_arg_type: Optional[RType] = None,
368396
steals: StealsDescription = False,
369397
priority: int = 1) -> CFunctionDescription:
398+
"""Define a c function call op for a binary operation.
399+
400+
This will be automatically generated by matching against the AST.
401+
402+
Most arguments are similar to c_method_op(), but exactly two argument types
403+
are expected.
404+
"""
370405
ops = c_binary_ops.setdefault(name, [])
371-
desc = CFunctionDescription(name, arg_types, return_type,
406+
desc = CFunctionDescription(name, arg_types, return_type, var_arg_type,
372407
c_function_name, error_kind, steals, priority)
373408
ops.append(desc)
374409
return desc
375410

376411

412+
def c_custom_op(arg_types: List[RType],
413+
return_type: RType,
414+
c_function_name: str,
415+
error_kind: int,
416+
var_arg_type: Optional[RType] = None,
417+
steals: StealsDescription = False) -> CFunctionDescription:
418+
"""Create a one-off CallC op that can't be automatically generated from the AST.
419+
420+
Most arguments are similar to c_method_op().
421+
"""
422+
return CFunctionDescription('<custom>', arg_types, return_type, var_arg_type,
423+
c_function_name, error_kind, steals, 0)
424+
425+
377426
def c_unary_op(name: str,
378427
arg_type: RType,
379428
return_type: RType,
380429
c_function_name: str,
381430
error_kind: int,
382431
steals: StealsDescription = False,
383432
priority: int = 1) -> CFunctionDescription:
433+
"""Define a c function call op for an unary operation.
434+
435+
This will be automatically generated by matching against the AST.
436+
437+
Most arguments are similar to c_method_op(), but exactly one argument type
438+
is expected.
439+
"""
384440
ops = c_unary_ops.setdefault(name, [])
385-
desc = CFunctionDescription(name, [arg_type], return_type,
441+
desc = CFunctionDescription(name, [arg_type], return_type, None,
386442
c_function_name, error_kind, steals, priority)
387443
ops.append(desc)
388444
return desc
389445

446+
390447
# Import various modules that set up global state.
391448
import mypyc.primitives.int_ops # noqa
392449
import mypyc.primitives.str_ops # noqa

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy