ast_helpers.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. """IRBuilder AST transform helpers shared between expressions and statements.
  2. Shared code that is tightly coupled to mypy ASTs can be put here instead of
  3. making mypyc.irbuild.builder larger.
  4. """
  5. from __future__ import annotations
  6. from mypy.nodes import (
  7. LDEF,
  8. BytesExpr,
  9. ComparisonExpr,
  10. Expression,
  11. FloatExpr,
  12. IntExpr,
  13. MemberExpr,
  14. NameExpr,
  15. OpExpr,
  16. StrExpr,
  17. UnaryExpr,
  18. Var,
  19. )
  20. from mypyc.ir.ops import BasicBlock
  21. from mypyc.ir.rtypes import is_fixed_width_rtype, is_tagged
  22. from mypyc.irbuild.builder import IRBuilder
  23. from mypyc.irbuild.constant_fold import constant_fold_expr
  24. def process_conditional(
  25. self: IRBuilder, e: Expression, true: BasicBlock, false: BasicBlock
  26. ) -> None:
  27. if isinstance(e, OpExpr) and e.op in ["and", "or"]:
  28. if e.op == "and":
  29. # Short circuit 'and' in a conditional context.
  30. new = BasicBlock()
  31. process_conditional(self, e.left, new, false)
  32. self.activate_block(new)
  33. process_conditional(self, e.right, true, false)
  34. else:
  35. # Short circuit 'or' in a conditional context.
  36. new = BasicBlock()
  37. process_conditional(self, e.left, true, new)
  38. self.activate_block(new)
  39. process_conditional(self, e.right, true, false)
  40. elif isinstance(e, UnaryExpr) and e.op == "not":
  41. process_conditional(self, e.expr, false, true)
  42. else:
  43. res = maybe_process_conditional_comparison(self, e, true, false)
  44. if res:
  45. return
  46. # Catch-all for arbitrary expressions.
  47. reg = self.accept(e)
  48. self.add_bool_branch(reg, true, false)
  49. def maybe_process_conditional_comparison(
  50. self: IRBuilder, e: Expression, true: BasicBlock, false: BasicBlock
  51. ) -> bool:
  52. """Transform simple tagged integer comparisons in a conditional context.
  53. Return True if the operation is supported (and was transformed). Otherwise,
  54. do nothing and return False.
  55. Args:
  56. e: Arbitrary expression
  57. true: Branch target if comparison is true
  58. false: Branch target if comparison is false
  59. """
  60. if not isinstance(e, ComparisonExpr) or len(e.operands) != 2:
  61. return False
  62. ltype = self.node_type(e.operands[0])
  63. rtype = self.node_type(e.operands[1])
  64. if not (
  65. (is_tagged(ltype) or is_fixed_width_rtype(ltype))
  66. and (is_tagged(rtype) or is_fixed_width_rtype(rtype))
  67. ):
  68. return False
  69. op = e.operators[0]
  70. if op not in ("==", "!=", "<", "<=", ">", ">="):
  71. return False
  72. left_expr = e.operands[0]
  73. right_expr = e.operands[1]
  74. borrow_left = is_borrow_friendly_expr(self, right_expr)
  75. left = self.accept(left_expr, can_borrow=borrow_left)
  76. right = self.accept(right_expr, can_borrow=True)
  77. if is_fixed_width_rtype(ltype) or is_fixed_width_rtype(rtype):
  78. if not is_fixed_width_rtype(ltype):
  79. left = self.coerce(left, rtype, e.line)
  80. elif not is_fixed_width_rtype(rtype):
  81. right = self.coerce(right, ltype, e.line)
  82. reg = self.binary_op(left, right, op, e.line)
  83. self.builder.flush_keep_alives()
  84. self.add_bool_branch(reg, true, false)
  85. else:
  86. # "left op right" for two tagged integers
  87. self.builder.compare_tagged_condition(left, right, op, true, false, e.line)
  88. return True
  89. def is_borrow_friendly_expr(self: IRBuilder, expr: Expression) -> bool:
  90. """Can the result of the expression borrowed temporarily?
  91. Borrowing means keeping a reference without incrementing the reference count.
  92. """
  93. if isinstance(expr, (IntExpr, FloatExpr, StrExpr, BytesExpr)):
  94. # Literals are immortal and can always be borrowed
  95. return True
  96. if (
  97. isinstance(expr, (UnaryExpr, OpExpr, NameExpr, MemberExpr))
  98. and constant_fold_expr(self, expr) is not None
  99. ):
  100. # Literal expressions are similar to literals
  101. return True
  102. if isinstance(expr, NameExpr):
  103. if isinstance(expr.node, Var) and expr.kind == LDEF:
  104. # Local variable reference can be borrowed
  105. return True
  106. if isinstance(expr, MemberExpr) and self.is_native_attr_ref(expr):
  107. return True
  108. return False