for_helpers.py 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046
  1. """Helpers for generating for loops and comprehensions.
  2. We special case certain kinds for loops such as "for x in range(...)"
  3. for better efficiency. Each for loop generator class below deals one
  4. such special case.
  5. """
  6. from __future__ import annotations
  7. from typing import Callable, ClassVar
  8. from mypy.nodes import (
  9. ARG_POS,
  10. CallExpr,
  11. Expression,
  12. GeneratorExpr,
  13. Lvalue,
  14. MemberExpr,
  15. RefExpr,
  16. SetExpr,
  17. TupleExpr,
  18. TypeAlias,
  19. )
  20. from mypyc.ir.ops import (
  21. BasicBlock,
  22. Branch,
  23. Integer,
  24. IntOp,
  25. LoadAddress,
  26. LoadMem,
  27. Register,
  28. TupleGet,
  29. TupleSet,
  30. Value,
  31. )
  32. from mypyc.ir.rtypes import (
  33. RTuple,
  34. RType,
  35. bool_rprimitive,
  36. int_rprimitive,
  37. is_dict_rprimitive,
  38. is_fixed_width_rtype,
  39. is_list_rprimitive,
  40. is_sequence_rprimitive,
  41. is_short_int_rprimitive,
  42. is_str_rprimitive,
  43. is_tuple_rprimitive,
  44. pointer_rprimitive,
  45. short_int_rprimitive,
  46. )
  47. from mypyc.irbuild.builder import IRBuilder
  48. from mypyc.irbuild.targets import AssignmentTarget, AssignmentTargetTuple
  49. from mypyc.primitives.dict_ops import (
  50. dict_check_size_op,
  51. dict_item_iter_op,
  52. dict_key_iter_op,
  53. dict_next_item_op,
  54. dict_next_key_op,
  55. dict_next_value_op,
  56. dict_value_iter_op,
  57. )
  58. from mypyc.primitives.exc_ops import no_err_occurred_op
  59. from mypyc.primitives.generic_ops import aiter_op, anext_op, iter_op, next_op
  60. from mypyc.primitives.list_ops import list_append_op, list_get_item_unsafe_op, new_list_set_item_op
  61. from mypyc.primitives.misc_ops import stop_async_iteration_op
  62. from mypyc.primitives.registry import CFunctionDescription
  63. from mypyc.primitives.set_ops import set_add_op
  64. GenFunc = Callable[[], None]
  65. def for_loop_helper(
  66. builder: IRBuilder,
  67. index: Lvalue,
  68. expr: Expression,
  69. body_insts: GenFunc,
  70. else_insts: GenFunc | None,
  71. is_async: bool,
  72. line: int,
  73. ) -> None:
  74. """Generate IR for a loop.
  75. Args:
  76. index: the loop index Lvalue
  77. expr: the expression to iterate over
  78. body_insts: a function that generates the body of the loop
  79. else_insts: a function that generates the else block instructions
  80. """
  81. # Body of the loop
  82. body_block = BasicBlock()
  83. # Block that steps to the next item
  84. step_block = BasicBlock()
  85. # Block for the else clause, if we need it
  86. else_block = BasicBlock()
  87. # Block executed after the loop
  88. exit_block = BasicBlock()
  89. # Determine where we want to exit, if our condition check fails.
  90. normal_loop_exit = else_block if else_insts is not None else exit_block
  91. for_gen = make_for_loop_generator(
  92. builder, index, expr, body_block, normal_loop_exit, line, is_async=is_async
  93. )
  94. builder.push_loop_stack(step_block, exit_block)
  95. condition_block = BasicBlock()
  96. builder.goto_and_activate(condition_block)
  97. # Add loop condition check.
  98. for_gen.gen_condition()
  99. # Generate loop body.
  100. builder.activate_block(body_block)
  101. for_gen.begin_body()
  102. body_insts()
  103. # We generate a separate step block (which might be empty).
  104. builder.goto_and_activate(step_block)
  105. for_gen.gen_step()
  106. # Go back to loop condition.
  107. builder.goto(condition_block)
  108. for_gen.add_cleanup(normal_loop_exit)
  109. builder.pop_loop_stack()
  110. if else_insts is not None:
  111. builder.activate_block(else_block)
  112. else_insts()
  113. builder.goto(exit_block)
  114. builder.activate_block(exit_block)
  115. def for_loop_helper_with_index(
  116. builder: IRBuilder,
  117. index: Lvalue,
  118. expr: Expression,
  119. expr_reg: Value,
  120. body_insts: Callable[[Value], None],
  121. line: int,
  122. ) -> None:
  123. """Generate IR for a sequence iteration.
  124. This function only works for sequence type. Compared to for_loop_helper,
  125. it would feed iteration index to body_insts.
  126. Args:
  127. index: the loop index Lvalue
  128. expr: the expression to iterate over
  129. body_insts: a function that generates the body of the loop.
  130. It needs a index as parameter.
  131. """
  132. assert is_sequence_rprimitive(expr_reg.type)
  133. target_type = builder.get_sequence_type(expr)
  134. body_block = BasicBlock()
  135. step_block = BasicBlock()
  136. exit_block = BasicBlock()
  137. condition_block = BasicBlock()
  138. for_gen = ForSequence(builder, index, body_block, exit_block, line, False)
  139. for_gen.init(expr_reg, target_type, reverse=False)
  140. builder.push_loop_stack(step_block, exit_block)
  141. builder.goto_and_activate(condition_block)
  142. for_gen.gen_condition()
  143. builder.activate_block(body_block)
  144. for_gen.begin_body()
  145. body_insts(builder.read(for_gen.index_target))
  146. builder.goto_and_activate(step_block)
  147. for_gen.gen_step()
  148. builder.goto(condition_block)
  149. for_gen.add_cleanup(exit_block)
  150. builder.pop_loop_stack()
  151. builder.activate_block(exit_block)
  152. def sequence_from_generator_preallocate_helper(
  153. builder: IRBuilder,
  154. gen: GeneratorExpr,
  155. empty_op_llbuilder: Callable[[Value, int], Value],
  156. set_item_op: CFunctionDescription,
  157. ) -> Value | None:
  158. """Generate a new tuple or list from a simple generator expression.
  159. Currently we only optimize for simplest generator expression, which means that
  160. there is no condition list in the generator and only one original sequence with
  161. one index is allowed.
  162. e.g. (1) tuple(f(x) for x in a_list/a_tuple)
  163. (2) list(f(x) for x in a_list/a_tuple)
  164. (3) [f(x) for x in a_list/a_tuple]
  165. RTuple as an original sequence is not supported yet.
  166. Args:
  167. empty_op_llbuilder: A function that can generate an empty sequence op when
  168. passed in length. See `new_list_op_with_length` and `new_tuple_op_with_length`
  169. for detailed implementation.
  170. set_item_op: A primitive that can modify an arbitrary position of a sequence.
  171. The op should have three arguments:
  172. - Self
  173. - Target position
  174. - New Value
  175. See `new_list_set_item_op` and `new_tuple_set_item_op` for detailed
  176. implementation.
  177. """
  178. if len(gen.sequences) == 1 and len(gen.indices) == 1 and len(gen.condlists[0]) == 0:
  179. rtype = builder.node_type(gen.sequences[0])
  180. if is_list_rprimitive(rtype) or is_tuple_rprimitive(rtype) or is_str_rprimitive(rtype):
  181. sequence = builder.accept(gen.sequences[0])
  182. length = builder.builder.builtin_len(sequence, gen.line, use_pyssize_t=True)
  183. target_op = empty_op_llbuilder(length, gen.line)
  184. def set_item(item_index: Value) -> None:
  185. e = builder.accept(gen.left_expr)
  186. builder.call_c(set_item_op, [target_op, item_index, e], gen.line)
  187. for_loop_helper_with_index(
  188. builder, gen.indices[0], gen.sequences[0], sequence, set_item, gen.line
  189. )
  190. return target_op
  191. return None
  192. def translate_list_comprehension(builder: IRBuilder, gen: GeneratorExpr) -> Value:
  193. # Try simplest list comprehension, otherwise fall back to general one
  194. val = sequence_from_generator_preallocate_helper(
  195. builder,
  196. gen,
  197. empty_op_llbuilder=builder.builder.new_list_op_with_length,
  198. set_item_op=new_list_set_item_op,
  199. )
  200. if val is not None:
  201. return val
  202. list_ops = builder.maybe_spill(builder.new_list_op([], gen.line))
  203. loop_params = list(zip(gen.indices, gen.sequences, gen.condlists, gen.is_async))
  204. def gen_inner_stmts() -> None:
  205. e = builder.accept(gen.left_expr)
  206. builder.call_c(list_append_op, [builder.read(list_ops), e], gen.line)
  207. comprehension_helper(builder, loop_params, gen_inner_stmts, gen.line)
  208. return builder.read(list_ops)
  209. def translate_set_comprehension(builder: IRBuilder, gen: GeneratorExpr) -> Value:
  210. set_ops = builder.maybe_spill(builder.new_set_op([], gen.line))
  211. loop_params = list(zip(gen.indices, gen.sequences, gen.condlists, gen.is_async))
  212. def gen_inner_stmts() -> None:
  213. e = builder.accept(gen.left_expr)
  214. builder.call_c(set_add_op, [builder.read(set_ops), e], gen.line)
  215. comprehension_helper(builder, loop_params, gen_inner_stmts, gen.line)
  216. return builder.read(set_ops)
  217. def comprehension_helper(
  218. builder: IRBuilder,
  219. loop_params: list[tuple[Lvalue, Expression, list[Expression], bool]],
  220. gen_inner_stmts: Callable[[], None],
  221. line: int,
  222. ) -> None:
  223. """Helper function for list comprehensions.
  224. Args:
  225. loop_params: a list of (index, expr, [conditions]) tuples defining nested loops:
  226. - "index" is the Lvalue indexing that loop;
  227. - "expr" is the expression for the object to be iterated over;
  228. - "conditions" is a list of conditions, evaluated in order with short-circuiting,
  229. that must all be true for the loop body to be executed
  230. gen_inner_stmts: function to generate the IR for the body of the innermost loop
  231. """
  232. def handle_loop(loop_params: list[tuple[Lvalue, Expression, list[Expression], bool]]) -> None:
  233. """Generate IR for a loop.
  234. Given a list of (index, expression, [conditions]) tuples, generate IR
  235. for the nested loops the list defines.
  236. """
  237. index, expr, conds, is_async = loop_params[0]
  238. for_loop_helper(
  239. builder,
  240. index,
  241. expr,
  242. lambda: loop_contents(conds, loop_params[1:]),
  243. None,
  244. is_async=is_async,
  245. line=line,
  246. )
  247. def loop_contents(
  248. conds: list[Expression],
  249. remaining_loop_params: list[tuple[Lvalue, Expression, list[Expression], bool]],
  250. ) -> None:
  251. """Generate the body of the loop.
  252. Args:
  253. conds: a list of conditions to be evaluated (in order, with short circuiting)
  254. to gate the body of the loop
  255. remaining_loop_params: the parameters for any further nested loops; if it's empty
  256. we'll instead evaluate the "gen_inner_stmts" function
  257. """
  258. # Check conditions, in order, short circuiting them.
  259. for cond in conds:
  260. cond_val = builder.accept(cond)
  261. cont_block, rest_block = BasicBlock(), BasicBlock()
  262. # If the condition is true we'll skip the continue.
  263. builder.add_bool_branch(cond_val, rest_block, cont_block)
  264. builder.activate_block(cont_block)
  265. builder.nonlocal_control[-1].gen_continue(builder, cond.line)
  266. builder.goto_and_activate(rest_block)
  267. if remaining_loop_params:
  268. # There's another nested level, so the body of this loop is another loop.
  269. return handle_loop(remaining_loop_params)
  270. else:
  271. # We finally reached the actual body of the generator.
  272. # Generate the IR for the inner loop body.
  273. gen_inner_stmts()
  274. handle_loop(loop_params)
  275. def is_range_ref(expr: RefExpr) -> bool:
  276. return (
  277. expr.fullname == "builtins.range"
  278. or isinstance(expr.node, TypeAlias)
  279. and expr.fullname == "six.moves.xrange"
  280. )
  281. def make_for_loop_generator(
  282. builder: IRBuilder,
  283. index: Lvalue,
  284. expr: Expression,
  285. body_block: BasicBlock,
  286. loop_exit: BasicBlock,
  287. line: int,
  288. is_async: bool = False,
  289. nested: bool = False,
  290. ) -> ForGenerator:
  291. """Return helper object for generating a for loop over an iterable.
  292. If "nested" is True, this is a nested iterator such as "e" in "enumerate(e)".
  293. """
  294. # Do an async loop if needed. async is always generic
  295. if is_async:
  296. expr_reg = builder.accept(expr)
  297. async_obj = ForAsyncIterable(builder, index, body_block, loop_exit, line, nested)
  298. item_type = builder._analyze_iterable_item_type(expr)
  299. item_rtype = builder.type_to_rtype(item_type)
  300. async_obj.init(expr_reg, item_rtype)
  301. return async_obj
  302. rtyp = builder.node_type(expr)
  303. if is_sequence_rprimitive(rtyp):
  304. # Special case "for x in <list>".
  305. expr_reg = builder.accept(expr)
  306. target_type = builder.get_sequence_type(expr)
  307. for_list = ForSequence(builder, index, body_block, loop_exit, line, nested)
  308. for_list.init(expr_reg, target_type, reverse=False)
  309. return for_list
  310. if is_dict_rprimitive(rtyp):
  311. # Special case "for k in <dict>".
  312. expr_reg = builder.accept(expr)
  313. target_type = builder.get_dict_key_type(expr)
  314. for_dict = ForDictionaryKeys(builder, index, body_block, loop_exit, line, nested)
  315. for_dict.init(expr_reg, target_type)
  316. return for_dict
  317. if isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr):
  318. if (
  319. is_range_ref(expr.callee)
  320. and (
  321. len(expr.args) <= 2
  322. or (len(expr.args) == 3 and builder.extract_int(expr.args[2]) is not None)
  323. )
  324. and set(expr.arg_kinds) == {ARG_POS}
  325. ):
  326. # Special case "for x in range(...)".
  327. # We support the 3 arg form but only for int literals, since it doesn't
  328. # seem worth the hassle of supporting dynamically determining which
  329. # direction of comparison to do.
  330. if len(expr.args) == 1:
  331. start_reg: Value = Integer(0)
  332. end_reg = builder.accept(expr.args[0])
  333. else:
  334. start_reg = builder.accept(expr.args[0])
  335. end_reg = builder.accept(expr.args[1])
  336. if len(expr.args) == 3:
  337. step = builder.extract_int(expr.args[2])
  338. assert step is not None
  339. if step == 0:
  340. builder.error("range() step can't be zero", expr.args[2].line)
  341. else:
  342. step = 1
  343. for_range = ForRange(builder, index, body_block, loop_exit, line, nested)
  344. for_range.init(start_reg, end_reg, step)
  345. return for_range
  346. elif (
  347. expr.callee.fullname == "builtins.enumerate"
  348. and len(expr.args) == 1
  349. and expr.arg_kinds == [ARG_POS]
  350. and isinstance(index, TupleExpr)
  351. and len(index.items) == 2
  352. ):
  353. # Special case "for i, x in enumerate(y)".
  354. lvalue1 = index.items[0]
  355. lvalue2 = index.items[1]
  356. for_enumerate = ForEnumerate(builder, index, body_block, loop_exit, line, nested)
  357. for_enumerate.init(lvalue1, lvalue2, expr.args[0])
  358. return for_enumerate
  359. elif (
  360. expr.callee.fullname == "builtins.zip"
  361. and len(expr.args) >= 2
  362. and set(expr.arg_kinds) == {ARG_POS}
  363. and isinstance(index, TupleExpr)
  364. and len(index.items) == len(expr.args)
  365. ):
  366. # Special case "for x, y in zip(a, b)".
  367. for_zip = ForZip(builder, index, body_block, loop_exit, line, nested)
  368. for_zip.init(index.items, expr.args)
  369. return for_zip
  370. if (
  371. expr.callee.fullname == "builtins.reversed"
  372. and len(expr.args) == 1
  373. and expr.arg_kinds == [ARG_POS]
  374. and is_sequence_rprimitive(builder.node_type(expr.args[0]))
  375. ):
  376. # Special case "for x in reversed(<list>)".
  377. expr_reg = builder.accept(expr.args[0])
  378. target_type = builder.get_sequence_type(expr)
  379. for_list = ForSequence(builder, index, body_block, loop_exit, line, nested)
  380. for_list.init(expr_reg, target_type, reverse=True)
  381. return for_list
  382. if isinstance(expr, CallExpr) and isinstance(expr.callee, MemberExpr) and not expr.args:
  383. # Special cases for dictionary iterator methods, like dict.items().
  384. rtype = builder.node_type(expr.callee.expr)
  385. if is_dict_rprimitive(rtype) and expr.callee.name in ("keys", "values", "items"):
  386. expr_reg = builder.accept(expr.callee.expr)
  387. for_dict_type: type[ForGenerator] | None = None
  388. if expr.callee.name == "keys":
  389. target_type = builder.get_dict_key_type(expr.callee.expr)
  390. for_dict_type = ForDictionaryKeys
  391. elif expr.callee.name == "values":
  392. target_type = builder.get_dict_value_type(expr.callee.expr)
  393. for_dict_type = ForDictionaryValues
  394. else:
  395. target_type = builder.get_dict_item_type(expr.callee.expr)
  396. for_dict_type = ForDictionaryItems
  397. for_dict_gen = for_dict_type(builder, index, body_block, loop_exit, line, nested)
  398. for_dict_gen.init(expr_reg, target_type)
  399. return for_dict_gen
  400. iterable_expr_reg: Value | None = None
  401. if isinstance(expr, SetExpr):
  402. # Special case "for x in <set literal>".
  403. from mypyc.irbuild.expression import precompute_set_literal
  404. set_literal = precompute_set_literal(builder, expr)
  405. if set_literal is not None:
  406. iterable_expr_reg = set_literal
  407. # Default to a generic for loop.
  408. if iterable_expr_reg is None:
  409. iterable_expr_reg = builder.accept(expr)
  410. for_obj = ForIterable(builder, index, body_block, loop_exit, line, nested)
  411. item_type = builder._analyze_iterable_item_type(expr)
  412. item_rtype = builder.type_to_rtype(item_type)
  413. for_obj.init(iterable_expr_reg, item_rtype)
  414. return for_obj
  415. class ForGenerator:
  416. """Abstract base class for generating for loops."""
  417. def __init__(
  418. self,
  419. builder: IRBuilder,
  420. index: Lvalue,
  421. body_block: BasicBlock,
  422. loop_exit: BasicBlock,
  423. line: int,
  424. nested: bool,
  425. ) -> None:
  426. self.builder = builder
  427. self.index = index
  428. self.body_block = body_block
  429. self.line = line
  430. # Some for loops need a cleanup block that we execute at exit. We
  431. # create a cleanup block if needed. However, if we are generating a for
  432. # loop for a nested iterator, such as "e" in "enumerate(e)", the
  433. # outermost generator should generate the cleanup block -- we don't
  434. # need to do it here.
  435. if self.need_cleanup() and not nested:
  436. # Create a new block to handle cleanup after loop exit.
  437. self.loop_exit = BasicBlock()
  438. else:
  439. # Just use the existing loop exit block.
  440. self.loop_exit = loop_exit
  441. def need_cleanup(self) -> bool:
  442. """If this returns true, we need post-loop cleanup."""
  443. return False
  444. def add_cleanup(self, exit_block: BasicBlock) -> None:
  445. """Add post-loop cleanup, if needed."""
  446. if self.need_cleanup():
  447. self.builder.activate_block(self.loop_exit)
  448. self.gen_cleanup()
  449. self.builder.goto(exit_block)
  450. def gen_condition(self) -> None:
  451. """Generate check for loop exit (e.g. exhaustion of iteration)."""
  452. def begin_body(self) -> None:
  453. """Generate ops at the beginning of the body (if needed)."""
  454. def gen_step(self) -> None:
  455. """Generate stepping to the next item (if needed)."""
  456. def gen_cleanup(self) -> None:
  457. """Generate post-loop cleanup (if needed)."""
  458. def load_len(self, expr: Value | AssignmentTarget) -> Value:
  459. """A helper to get collection length, used by several subclasses."""
  460. return self.builder.builder.builtin_len(self.builder.read(expr, self.line), self.line)
  461. class ForIterable(ForGenerator):
  462. """Generate IR for a for loop over an arbitrary iterable (the general case)."""
  463. def need_cleanup(self) -> bool:
  464. # Create a new cleanup block for when the loop is finished.
  465. return True
  466. def init(self, expr_reg: Value, target_type: RType) -> None:
  467. # Define targets to contain the expression, along with the iterator that will be used
  468. # for the for-loop. If we are inside of a generator function, spill these into the
  469. # environment class.
  470. builder = self.builder
  471. iter_reg = builder.call_c(iter_op, [expr_reg], self.line)
  472. builder.maybe_spill(expr_reg)
  473. self.iter_target = builder.maybe_spill(iter_reg)
  474. self.target_type = target_type
  475. def gen_condition(self) -> None:
  476. # We call __next__ on the iterator and check to see if the return value
  477. # is NULL, which signals either the end of the Iterable being traversed
  478. # or an exception being raised. Note that Branch.IS_ERROR checks only
  479. # for NULL (an exception does not necessarily have to be raised).
  480. builder = self.builder
  481. line = self.line
  482. self.next_reg = builder.call_c(next_op, [builder.read(self.iter_target, line)], line)
  483. builder.add(Branch(self.next_reg, self.loop_exit, self.body_block, Branch.IS_ERROR))
  484. def begin_body(self) -> None:
  485. # Assign the value obtained from __next__ to the
  486. # lvalue so that it can be referenced by code in the body of the loop.
  487. builder = self.builder
  488. line = self.line
  489. # We unbox here so that iterating with tuple unpacking generates a tuple based
  490. # unpack instead of an iterator based one.
  491. next_reg = builder.coerce(self.next_reg, self.target_type, line)
  492. builder.assign(builder.get_assignment_target(self.index), next_reg, line)
  493. def gen_step(self) -> None:
  494. # Nothing to do here, since we get the next item as part of gen_condition().
  495. pass
  496. def gen_cleanup(self) -> None:
  497. # We set the branch to go here if the conditional evaluates to true. If
  498. # an exception was raised during the loop, then err_reg will be set to
  499. # True. If no_err_occurred_op returns False, then the exception will be
  500. # propagated using the ERR_FALSE flag.
  501. self.builder.call_c(no_err_occurred_op, [], self.line)
  502. class ForAsyncIterable(ForGenerator):
  503. """Generate IR for an async for loop."""
  504. def init(self, expr_reg: Value, target_type: RType) -> None:
  505. # Define targets to contain the expression, along with the
  506. # iterator that will be used for the for-loop. We are inside
  507. # of a generator function, so we will spill these into
  508. # environment class.
  509. builder = self.builder
  510. iter_reg = builder.call_c(aiter_op, [expr_reg], self.line)
  511. builder.maybe_spill(expr_reg)
  512. self.iter_target = builder.maybe_spill(iter_reg)
  513. self.target_type = target_type
  514. self.stop_reg = Register(bool_rprimitive)
  515. def gen_condition(self) -> None:
  516. # This does the test and fetches the next value
  517. # try:
  518. # TARGET = await type(iter).__anext__(iter)
  519. # stop = False
  520. # except StopAsyncIteration:
  521. # stop = True
  522. #
  523. # What a pain.
  524. # There are optimizations available here if we punch through some abstractions.
  525. from mypyc.irbuild.statement import emit_await, transform_try_except
  526. builder = self.builder
  527. line = self.line
  528. def except_match() -> Value:
  529. addr = builder.add(LoadAddress(pointer_rprimitive, stop_async_iteration_op.src, line))
  530. return builder.add(LoadMem(stop_async_iteration_op.type, addr))
  531. def try_body() -> None:
  532. awaitable = builder.call_c(anext_op, [builder.read(self.iter_target)], line)
  533. self.next_reg = emit_await(builder, awaitable, line)
  534. builder.assign(self.stop_reg, builder.false(), -1)
  535. def except_body() -> None:
  536. builder.assign(self.stop_reg, builder.true(), line)
  537. transform_try_except(
  538. builder, try_body, [((except_match, line), None, except_body)], None, line
  539. )
  540. builder.add(Branch(self.stop_reg, self.loop_exit, self.body_block, Branch.BOOL))
  541. def begin_body(self) -> None:
  542. # Assign the value obtained from await __anext__ to the
  543. # lvalue so that it can be referenced by code in the body of the loop.
  544. builder = self.builder
  545. line = self.line
  546. # We unbox here so that iterating with tuple unpacking generates a tuple based
  547. # unpack instead of an iterator based one.
  548. next_reg = builder.coerce(self.next_reg, self.target_type, line)
  549. builder.assign(builder.get_assignment_target(self.index), next_reg, line)
  550. def gen_step(self) -> None:
  551. # Nothing to do here, since we get the next item as part of gen_condition().
  552. pass
  553. def unsafe_index(builder: IRBuilder, target: Value, index: Value, line: int) -> Value:
  554. """Emit a potentially unsafe index into a target."""
  555. # This doesn't really fit nicely into any of our data-driven frameworks
  556. # since we want to use __getitem__ if we don't have an unsafe version,
  557. # so we just check manually.
  558. if is_list_rprimitive(target.type):
  559. return builder.call_c(list_get_item_unsafe_op, [target, index], line)
  560. else:
  561. return builder.gen_method_call(target, "__getitem__", [index], None, line)
  562. class ForSequence(ForGenerator):
  563. """Generate optimized IR for a for loop over a sequence.
  564. Supports iterating in both forward and reverse.
  565. """
  566. def init(self, expr_reg: Value, target_type: RType, reverse: bool) -> None:
  567. builder = self.builder
  568. self.reverse = reverse
  569. # Define target to contain the expression, along with the index that will be used
  570. # for the for-loop. If we are inside of a generator function, spill these into the
  571. # environment class.
  572. self.expr_target = builder.maybe_spill(expr_reg)
  573. if not reverse:
  574. index_reg: Value = Integer(0)
  575. else:
  576. index_reg = builder.binary_op(
  577. self.load_len(self.expr_target), Integer(1), "-", self.line
  578. )
  579. self.index_target = builder.maybe_spill_assignable(index_reg)
  580. self.target_type = target_type
  581. def gen_condition(self) -> None:
  582. builder = self.builder
  583. line = self.line
  584. # TODO: Don't reload the length each time when iterating an immutable sequence?
  585. if self.reverse:
  586. # If we are iterating in reverse order, we obviously need
  587. # to check that the index is still positive. Somewhat less
  588. # obviously we still need to check against the length,
  589. # since it could shrink out from under us.
  590. comparison = builder.binary_op(
  591. builder.read(self.index_target, line), Integer(0), ">=", line
  592. )
  593. second_check = BasicBlock()
  594. builder.add_bool_branch(comparison, second_check, self.loop_exit)
  595. builder.activate_block(second_check)
  596. # For compatibility with python semantics we recalculate the length
  597. # at every iteration.
  598. len_reg = self.load_len(self.expr_target)
  599. comparison = builder.binary_op(builder.read(self.index_target, line), len_reg, "<", line)
  600. builder.add_bool_branch(comparison, self.body_block, self.loop_exit)
  601. def begin_body(self) -> None:
  602. builder = self.builder
  603. line = self.line
  604. # Read the next list item.
  605. value_box = unsafe_index(
  606. builder,
  607. builder.read(self.expr_target, line),
  608. builder.read(self.index_target, line),
  609. line,
  610. )
  611. assert value_box
  612. # We coerce to the type of list elements here so that
  613. # iterating with tuple unpacking generates a tuple based
  614. # unpack instead of an iterator based one.
  615. builder.assign(
  616. builder.get_assignment_target(self.index),
  617. builder.coerce(value_box, self.target_type, line),
  618. line,
  619. )
  620. def gen_step(self) -> None:
  621. # Step to the next item.
  622. builder = self.builder
  623. line = self.line
  624. step = 1 if not self.reverse else -1
  625. add = builder.int_op(
  626. short_int_rprimitive,
  627. builder.read(self.index_target, line),
  628. Integer(step),
  629. IntOp.ADD,
  630. line,
  631. )
  632. builder.assign(self.index_target, add, line)
  633. class ForDictionaryCommon(ForGenerator):
  634. """Generate optimized IR for a for loop over dictionary keys/values.
  635. The logic is pretty straightforward, we use PyDict_Next() API wrapped in
  636. a tuple, so that we can modify only a single register. The layout of the tuple:
  637. * f0: are there more items (bool)
  638. * f1: current offset (int)
  639. * f2: next key (object)
  640. * f3: next value (object)
  641. For more info see https://docs.python.org/3/c-api/dict.html#c.PyDict_Next.
  642. Note that for subclasses we fall back to generic PyObject_GetIter() logic,
  643. since they may override some iteration methods in subtly incompatible manner.
  644. The fallback logic is implemented in CPy.h via dynamic type check.
  645. """
  646. dict_next_op: ClassVar[CFunctionDescription]
  647. dict_iter_op: ClassVar[CFunctionDescription]
  648. def need_cleanup(self) -> bool:
  649. # Technically, a dict subclass can raise an unrelated exception
  650. # in __next__(), so we need this.
  651. return True
  652. def init(self, expr_reg: Value, target_type: RType) -> None:
  653. builder = self.builder
  654. self.target_type = target_type
  655. # We add some variables to environment class, so they can be read across yield.
  656. self.expr_target = builder.maybe_spill(expr_reg)
  657. offset = Integer(0)
  658. self.offset_target = builder.maybe_spill_assignable(offset)
  659. self.size = builder.maybe_spill(self.load_len(self.expr_target))
  660. # For dict class (not a subclass) this is the dictionary itself.
  661. iter_reg = builder.call_c(self.dict_iter_op, [expr_reg], self.line)
  662. self.iter_target = builder.maybe_spill(iter_reg)
  663. def gen_condition(self) -> None:
  664. """Get next key/value pair, set new offset, and check if we should continue."""
  665. builder = self.builder
  666. line = self.line
  667. self.next_tuple = self.builder.call_c(
  668. self.dict_next_op,
  669. [builder.read(self.iter_target, line), builder.read(self.offset_target, line)],
  670. line,
  671. )
  672. # Do this here instead of in gen_step() to minimize variables in environment.
  673. new_offset = builder.add(TupleGet(self.next_tuple, 1, line))
  674. builder.assign(self.offset_target, new_offset, line)
  675. should_continue = builder.add(TupleGet(self.next_tuple, 0, line))
  676. builder.add(Branch(should_continue, self.body_block, self.loop_exit, Branch.BOOL))
  677. def gen_step(self) -> None:
  678. """Check that dictionary didn't change size during iteration.
  679. Raise RuntimeError if it is not the case to match CPython behavior.
  680. """
  681. builder = self.builder
  682. line = self.line
  683. # Technically, we don't need a new primitive for this, but it is simpler.
  684. builder.call_c(
  685. dict_check_size_op,
  686. [builder.read(self.expr_target, line), builder.read(self.size, line)],
  687. line,
  688. )
  689. def gen_cleanup(self) -> None:
  690. # Same as for generic ForIterable.
  691. self.builder.call_c(no_err_occurred_op, [], self.line)
  692. class ForDictionaryKeys(ForDictionaryCommon):
  693. """Generate optimized IR for a for loop over dictionary keys."""
  694. dict_next_op = dict_next_key_op
  695. dict_iter_op = dict_key_iter_op
  696. def begin_body(self) -> None:
  697. builder = self.builder
  698. line = self.line
  699. # Key is stored at the third place in the tuple.
  700. key = builder.add(TupleGet(self.next_tuple, 2, line))
  701. builder.assign(
  702. builder.get_assignment_target(self.index),
  703. builder.coerce(key, self.target_type, line),
  704. line,
  705. )
  706. class ForDictionaryValues(ForDictionaryCommon):
  707. """Generate optimized IR for a for loop over dictionary values."""
  708. dict_next_op = dict_next_value_op
  709. dict_iter_op = dict_value_iter_op
  710. def begin_body(self) -> None:
  711. builder = self.builder
  712. line = self.line
  713. # Value is stored at the third place in the tuple.
  714. value = builder.add(TupleGet(self.next_tuple, 2, line))
  715. builder.assign(
  716. builder.get_assignment_target(self.index),
  717. builder.coerce(value, self.target_type, line),
  718. line,
  719. )
  720. class ForDictionaryItems(ForDictionaryCommon):
  721. """Generate optimized IR for a for loop over dictionary items."""
  722. dict_next_op = dict_next_item_op
  723. dict_iter_op = dict_item_iter_op
  724. def begin_body(self) -> None:
  725. builder = self.builder
  726. line = self.line
  727. key = builder.add(TupleGet(self.next_tuple, 2, line))
  728. value = builder.add(TupleGet(self.next_tuple, 3, line))
  729. # Coerce just in case e.g. key is itself a tuple to be unpacked.
  730. assert isinstance(self.target_type, RTuple)
  731. key = builder.coerce(key, self.target_type.types[0], line)
  732. value = builder.coerce(value, self.target_type.types[1], line)
  733. target = builder.get_assignment_target(self.index)
  734. if isinstance(target, AssignmentTargetTuple):
  735. # Simpler code for common case: for k, v in d.items().
  736. if len(target.items) != 2:
  737. builder.error("Expected a pair for dict item iteration", line)
  738. builder.assign(target.items[0], key, line)
  739. builder.assign(target.items[1], value, line)
  740. else:
  741. rvalue = builder.add(TupleSet([key, value], line))
  742. builder.assign(target, rvalue, line)
  743. class ForRange(ForGenerator):
  744. """Generate optimized IR for a for loop over an integer range."""
  745. def init(self, start_reg: Value, end_reg: Value, step: int) -> None:
  746. builder = self.builder
  747. self.start_reg = start_reg
  748. self.end_reg = end_reg
  749. self.step = step
  750. self.end_target = builder.maybe_spill(end_reg)
  751. if is_short_int_rprimitive(start_reg.type) and is_short_int_rprimitive(end_reg.type):
  752. index_type: RType = short_int_rprimitive
  753. elif is_fixed_width_rtype(end_reg.type):
  754. index_type = end_reg.type
  755. else:
  756. index_type = int_rprimitive
  757. index_reg = Register(index_type)
  758. builder.assign(index_reg, start_reg, -1)
  759. self.index_reg = builder.maybe_spill_assignable(index_reg)
  760. # Initialize loop index to 0. Assert that the index target is assignable.
  761. self.index_target: Register | AssignmentTarget = builder.get_assignment_target(self.index)
  762. builder.assign(self.index_target, builder.read(self.index_reg, self.line), self.line)
  763. def gen_condition(self) -> None:
  764. builder = self.builder
  765. line = self.line
  766. # Add loop condition check.
  767. cmp = "<" if self.step > 0 else ">"
  768. comparison = builder.binary_op(
  769. builder.read(self.index_reg, line), builder.read(self.end_target, line), cmp, line
  770. )
  771. builder.add_bool_branch(comparison, self.body_block, self.loop_exit)
  772. def gen_step(self) -> None:
  773. builder = self.builder
  774. line = self.line
  775. # Increment index register. If the range is known to fit in short ints, use
  776. # short ints.
  777. if is_short_int_rprimitive(self.start_reg.type) and is_short_int_rprimitive(
  778. self.end_reg.type
  779. ):
  780. new_val = builder.int_op(
  781. short_int_rprimitive,
  782. builder.read(self.index_reg, line),
  783. Integer(self.step),
  784. IntOp.ADD,
  785. line,
  786. )
  787. else:
  788. new_val = builder.binary_op(
  789. builder.read(self.index_reg, line), Integer(self.step), "+", line
  790. )
  791. builder.assign(self.index_reg, new_val, line)
  792. builder.assign(self.index_target, new_val, line)
  793. class ForInfiniteCounter(ForGenerator):
  794. """Generate optimized IR for a for loop counting from 0 to infinity."""
  795. def init(self) -> None:
  796. builder = self.builder
  797. # Create a register to store the state of the loop index and
  798. # initialize this register along with the loop index to 0.
  799. zero = Integer(0)
  800. self.index_reg = builder.maybe_spill_assignable(zero)
  801. self.index_target: Register | AssignmentTarget = builder.get_assignment_target(self.index)
  802. builder.assign(self.index_target, zero, self.line)
  803. def gen_step(self) -> None:
  804. builder = self.builder
  805. line = self.line
  806. # We can safely assume that the integer is short, since we are not going to wrap
  807. # around a 63-bit integer.
  808. # NOTE: This would be questionable if short ints could be 32 bits.
  809. new_val = builder.int_op(
  810. short_int_rprimitive, builder.read(self.index_reg, line), Integer(1), IntOp.ADD, line
  811. )
  812. builder.assign(self.index_reg, new_val, line)
  813. builder.assign(self.index_target, new_val, line)
  814. class ForEnumerate(ForGenerator):
  815. """Generate optimized IR for a for loop of form "for i, x in enumerate(it)"."""
  816. def need_cleanup(self) -> bool:
  817. # The wrapped for loop might need cleanup. This might generate a
  818. # redundant cleanup block, but that's okay.
  819. return True
  820. def init(self, index1: Lvalue, index2: Lvalue, expr: Expression) -> None:
  821. # Count from 0 to infinity (for the index lvalue).
  822. self.index_gen = ForInfiniteCounter(
  823. self.builder, index1, self.body_block, self.loop_exit, self.line, nested=True
  824. )
  825. self.index_gen.init()
  826. # Iterate over the actual iterable.
  827. self.main_gen = make_for_loop_generator(
  828. self.builder, index2, expr, self.body_block, self.loop_exit, self.line, nested=True
  829. )
  830. def gen_condition(self) -> None:
  831. # No need for a check for the index generator, since it's unconditional.
  832. self.main_gen.gen_condition()
  833. def begin_body(self) -> None:
  834. self.index_gen.begin_body()
  835. self.main_gen.begin_body()
  836. def gen_step(self) -> None:
  837. self.index_gen.gen_step()
  838. self.main_gen.gen_step()
  839. def gen_cleanup(self) -> None:
  840. self.index_gen.gen_cleanup()
  841. self.main_gen.gen_cleanup()
  842. class ForZip(ForGenerator):
  843. """Generate IR for a for loop of form `for x, ... in zip(a, ...)`."""
  844. def need_cleanup(self) -> bool:
  845. # The wrapped for loops might need cleanup. We might generate a
  846. # redundant cleanup block, but that's okay.
  847. return True
  848. def init(self, indexes: list[Lvalue], exprs: list[Expression]) -> None:
  849. assert len(indexes) == len(exprs)
  850. # Condition check will require multiple basic blocks, since there will be
  851. # multiple conditions to check.
  852. self.cond_blocks = [BasicBlock() for _ in range(len(indexes) - 1)] + [self.body_block]
  853. self.gens: list[ForGenerator] = []
  854. for index, expr, next_block in zip(indexes, exprs, self.cond_blocks):
  855. gen = make_for_loop_generator(
  856. self.builder, index, expr, next_block, self.loop_exit, self.line, nested=True
  857. )
  858. self.gens.append(gen)
  859. def gen_condition(self) -> None:
  860. for i, gen in enumerate(self.gens):
  861. gen.gen_condition()
  862. if i < len(self.gens) - 1:
  863. self.builder.activate_block(self.cond_blocks[i])
  864. def begin_body(self) -> None:
  865. for gen in self.gens:
  866. gen.begin_body()
  867. def gen_step(self) -> None:
  868. for gen in self.gens:
  869. gen.gen_step()
  870. def gen_cleanup(self) -> None:
  871. for gen in self.gens:
  872. gen.gen_cleanup()