Skip to content

Commit ce24783

Browse files
authored
[mypyc] Refactor: extract builtin function specializers from genops (#8431)
This also gets rid of a cyclic import. Work on mypyc/mypyc#714.
1 parent d9e209f commit ce24783

File tree

2 files changed

+192
-181
lines changed

2 files changed

+192
-181
lines changed

mypyc/genops.py

Lines changed: 7 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
StarExpr, GDEF, ARG_POS, ARG_NAMED
2525
)
2626
from mypy.types import (
27-
Type, Instance, TupleType, AnyType, TypeOfAny, UninhabitedType, get_proper_type
27+
Type, Instance, TupleType, UninhabitedType, get_proper_type
2828
)
2929
from mypy.visitor import ExpressionVisitor, StatementVisitor
3030
from mypy.util import split_target
@@ -33,14 +33,12 @@
3333
from mypyc.prebuildvisitor import PreBuildVisitor
3434
from mypyc.ops import (
3535
BasicBlock, AssignmentTarget, AssignmentTargetRegister, AssignmentTargetIndex,
36-
AssignmentTargetAttr, AssignmentTargetTuple, Environment, LoadInt, RType, Value, Register, Op,
37-
FuncIR, Assign, Branch, RTuple, Unreachable,
38-
TupleGet, ClassIR, NonExtClassInfo, RInstance, GetAttr, SetAttr,
39-
LoadStatic, InitStatic, INVALID_FUNC_DEF, int_rprimitive,
40-
bool_rprimitive, list_rprimitive, is_list_rprimitive, dict_rprimitive, set_rprimitive,
41-
str_rprimitive, none_rprimitive, is_none_rprimitive, object_rprimitive,
42-
PrimitiveOp, OpDescription, is_object_rprimitive,
43-
FuncSignature, NAMESPACE_MODULE, RaiseStandardError, FuncDecl
36+
AssignmentTargetAttr, AssignmentTargetTuple, Environment, LoadInt, RType, Value,
37+
Register, Op, FuncIR, Assign, Branch, RTuple, Unreachable, TupleGet, ClassIR,
38+
NonExtClassInfo, RInstance, GetAttr, SetAttr, LoadStatic, InitStatic, INVALID_FUNC_DEF,
39+
int_rprimitive, is_list_rprimitive, dict_rprimitive, none_rprimitive,
40+
is_none_rprimitive, object_rprimitive, PrimitiveOp, OpDescription,
41+
is_object_rprimitive, FuncSignature, NAMESPACE_MODULE, RaiseStandardError, FuncDecl
4442
)
4543
from mypyc.ops_primitive import func_ops
4644
from mypyc.ops_list import list_append_op, list_len_op, new_list_op, to_list, list_pop_last
@@ -58,7 +56,6 @@
5856
from mypyc.genopscontext import FuncInfo, ImplicitClass
5957
from mypyc.genopsmapper import Mapper
6058
from mypyc.ir_builder import LowLevelIRBuilder
61-
from mypyc.specialize import specialize_function
6259

6360
GenFunc = Callable[[], None]
6461

@@ -1029,167 +1026,6 @@ def loop_contents(
10291026

10301027
handle_loop(loop_params)
10311028

1032-
# Builtin function special cases
1033-
1034-
@specialize_function('builtins.globals')
1035-
def translate_globals(self, expr: CallExpr, callee: RefExpr) -> Optional[Value]:
1036-
# Special case builtins.globals
1037-
if len(expr.args) == 0:
1038-
return self.load_globals_dict()
1039-
return None
1040-
1041-
@specialize_function('builtins.len')
1042-
def translate_len(
1043-
self, expr: CallExpr, callee: RefExpr) -> Optional[Value]:
1044-
# Special case builtins.len
1045-
if (len(expr.args) == 1
1046-
and expr.arg_kinds == [ARG_POS]):
1047-
expr_rtype = self.node_type(expr.args[0])
1048-
if isinstance(expr_rtype, RTuple):
1049-
# len() of fixed-length tuple can be trivially determined statically,
1050-
# though we still need to evaluate it.
1051-
self.accept(expr.args[0])
1052-
return self.add(LoadInt(len(expr_rtype.types)))
1053-
return None
1054-
1055-
# Special cases for things that consume iterators where we know we
1056-
# can safely compile a generator into a list.
1057-
@specialize_function('builtins.tuple')
1058-
@specialize_function('builtins.set')
1059-
@specialize_function('builtins.dict')
1060-
@specialize_function('builtins.sum')
1061-
@specialize_function('builtins.min')
1062-
@specialize_function('builtins.max')
1063-
@specialize_function('builtins.sorted')
1064-
@specialize_function('collections.OrderedDict')
1065-
@specialize_function('join', str_rprimitive)
1066-
@specialize_function('extend', list_rprimitive)
1067-
@specialize_function('update', dict_rprimitive)
1068-
@specialize_function('update', set_rprimitive)
1069-
def translate_safe_generator_call(self, expr: CallExpr, callee: RefExpr) -> Optional[Value]:
1070-
if (len(expr.args) > 0
1071-
and expr.arg_kinds[0] == ARG_POS
1072-
and isinstance(expr.args[0], GeneratorExpr)):
1073-
if isinstance(callee, MemberExpr):
1074-
return self.gen_method_call(
1075-
self.accept(callee.expr), callee.name,
1076-
([self.translate_list_comprehension(expr.args[0])]
1077-
+ [self.accept(arg) for arg in expr.args[1:]]),
1078-
self.node_type(expr), expr.line, expr.arg_kinds, expr.arg_names)
1079-
else:
1080-
return self.call_refexpr_with_args(
1081-
expr, callee,
1082-
([self.translate_list_comprehension(expr.args[0])]
1083-
+ [self.accept(arg) for arg in expr.args[1:]]))
1084-
return None
1085-
1086-
@specialize_function('builtins.any')
1087-
def translate_any_call(self, expr: CallExpr, callee: RefExpr) -> Optional[Value]:
1088-
if (len(expr.args) == 1
1089-
and expr.arg_kinds == [ARG_POS]
1090-
and isinstance(expr.args[0], GeneratorExpr)):
1091-
return self.any_all_helper(expr.args[0], false_op, lambda x: x, true_op)
1092-
return None
1093-
1094-
@specialize_function('builtins.all')
1095-
def translate_all_call(self, expr: CallExpr, callee: RefExpr) -> Optional[Value]:
1096-
if (len(expr.args) == 1
1097-
and expr.arg_kinds == [ARG_POS]
1098-
and isinstance(expr.args[0], GeneratorExpr)):
1099-
return self.any_all_helper(expr.args[0],
1100-
true_op,
1101-
lambda x: self.unary_op(x, 'not', expr.line),
1102-
false_op)
1103-
return None
1104-
1105-
# Special case for 'dataclasses.field' and 'attr.Factory' function calls
1106-
# because the results of such calls are typechecked by mypy using the types
1107-
# of the arguments to their respective functions, resulting in attempted
1108-
# coercions by mypyc that throw a runtime error.
1109-
@specialize_function('dataclasses.field')
1110-
@specialize_function('attr.Factory')
1111-
def translate_dataclasses_field_call(self, expr: CallExpr, callee: RefExpr) -> Optional[Value]:
1112-
self.types[expr] = AnyType(TypeOfAny.from_error)
1113-
return None
1114-
1115-
def any_all_helper(self,
1116-
gen: GeneratorExpr,
1117-
initial_value_op: OpDescription,
1118-
modify: Callable[[Value], Value],
1119-
new_value_op: OpDescription) -> Value:
1120-
retval = self.alloc_temp(bool_rprimitive)
1121-
self.assign(retval, self.primitive_op(initial_value_op, [], -1), -1)
1122-
loop_params = list(zip(gen.indices, gen.sequences, gen.condlists))
1123-
true_block, false_block, exit_block = BasicBlock(), BasicBlock(), BasicBlock()
1124-
1125-
def gen_inner_stmts() -> None:
1126-
comparison = modify(self.accept(gen.left_expr))
1127-
self.add_bool_branch(comparison, true_block, false_block)
1128-
self.activate_block(true_block)
1129-
self.assign(retval, self.primitive_op(new_value_op, [], -1), -1)
1130-
self.goto(exit_block)
1131-
self.activate_block(false_block)
1132-
1133-
self.comprehension_helper(loop_params, gen_inner_stmts, gen.line)
1134-
self.goto_and_activate(exit_block)
1135-
1136-
return retval
1137-
1138-
# Special case for calling next() on a generator expression, an
1139-
# idiom that shows up some in mypy.
1140-
#
1141-
# For example, next(x for x in l if x.id == 12, None) will
1142-
# generate code that searches l for an element where x.id == 12
1143-
# and produce the first such object, or None if no such element
1144-
# exists.
1145-
@specialize_function('builtins.next')
1146-
def translate_next_call(self, expr: CallExpr, callee: RefExpr) -> Optional[Value]:
1147-
if not (expr.arg_kinds in ([ARG_POS], [ARG_POS, ARG_POS])
1148-
and isinstance(expr.args[0], GeneratorExpr)):
1149-
return None
1150-
1151-
gen = expr.args[0]
1152-
1153-
retval = self.alloc_temp(self.node_type(expr))
1154-
default_val = None
1155-
if len(expr.args) > 1:
1156-
default_val = self.accept(expr.args[1])
1157-
1158-
exit_block = BasicBlock()
1159-
1160-
def gen_inner_stmts() -> None:
1161-
# next takes the first element of the generator, so if
1162-
# something gets produced, we are done.
1163-
self.assign(retval, self.accept(gen.left_expr), gen.left_expr.line)
1164-
self.goto(exit_block)
1165-
1166-
loop_params = list(zip(gen.indices, gen.sequences, gen.condlists))
1167-
self.comprehension_helper(loop_params, gen_inner_stmts, gen.line)
1168-
1169-
# Now we need the case for when nothing got hit. If there was
1170-
# a default value, we produce it, and otherwise we raise
1171-
# StopIteration.
1172-
if default_val:
1173-
self.assign(retval, default_val, gen.left_expr.line)
1174-
self.goto(exit_block)
1175-
else:
1176-
self.add(RaiseStandardError(RaiseStandardError.STOP_ITERATION, None, expr.line))
1177-
self.add(Unreachable())
1178-
1179-
self.activate_block(exit_block)
1180-
return retval
1181-
1182-
@specialize_function('builtins.isinstance')
1183-
def translate_isinstance(self, expr: CallExpr, callee: RefExpr) -> Optional[Value]:
1184-
# Special case builtins.isinstance
1185-
if (len(expr.args) == 2
1186-
and expr.arg_kinds == [ARG_POS, ARG_POS]
1187-
and isinstance(expr.args[1], (RefExpr, TupleExpr))):
1188-
irs = self.flatten_classes(expr.args[1])
1189-
if irs is not None:
1190-
return self.builder.isinstance_helper(self.accept(expr.args[0]), irs, expr.line)
1191-
return None
1192-
11931029
def flatten_classes(self, arg: Union[RefExpr, TupleExpr]) -> Optional[List[ClassIR]]:
11941030
"""Flatten classes in isinstance(obj, (A, (B, C))).
11951031

0 commit comments

Comments
 (0)