Generalized try/except/else handling that takes functions to gen the bodies. The point of this is to also be able to support with.
(
builder: IRBuilder,
body: GenFunc,
handlers: Sequence[tuple[tuple[ValueGenFunc, int] | None, Expression | None, GenFunc]],
else_body: GenFunc | None,
line: int,
)
| 667 | |
| 668 | |
| 669 | def transform_try_except( |
| 670 | builder: IRBuilder, |
| 671 | body: GenFunc, |
| 672 | handlers: Sequence[tuple[tuple[ValueGenFunc, int] | None, Expression | None, GenFunc]], |
| 673 | else_body: GenFunc | None, |
| 674 | line: int, |
| 675 | ) -> None: |
| 676 | """Generalized try/except/else handling that takes functions to gen the bodies. |
| 677 | |
| 678 | The point of this is to also be able to support with.""" |
| 679 | assert handlers, "try needs except" |
| 680 | |
| 681 | except_entry, exit_block, cleanup_block = BasicBlock(), BasicBlock(), BasicBlock() |
| 682 | double_except_block = BasicBlock() |
| 683 | # If there is an else block, jump there after the try, otherwise just leave |
| 684 | else_block = BasicBlock() if else_body else exit_block |
| 685 | |
| 686 | # Compile the try block with an error handler |
| 687 | builder.builder.push_error_handler(except_entry) |
| 688 | builder.goto_and_activate(BasicBlock()) |
| 689 | body() |
| 690 | builder.goto(else_block) |
| 691 | builder.builder.pop_error_handler() |
| 692 | |
| 693 | # The error handler catches the error and then checks it |
| 694 | # against the except clauses. We compile the error handler |
| 695 | # itself with an error handler so that it can properly restore |
| 696 | # the *old* exc_info if an exception occurs. |
| 697 | # The exception chaining will be done automatically when the |
| 698 | # exception is raised, based on the exception in exc_info. |
| 699 | builder.builder.push_error_handler(double_except_block) |
| 700 | builder.activate_block(except_entry) |
| 701 | old_exc = builder.maybe_spill(builder.call_c(error_catch_op, [], line)) |
| 702 | # Compile the except blocks with the nonlocal control flow overridden to clear exc_info |
| 703 | builder.nonlocal_control.append(ExceptNonlocalControl(builder.nonlocal_control[-1], old_exc)) |
| 704 | |
| 705 | # Process the bodies |
| 706 | for type, var, handler_body in handlers: |
| 707 | next_block = None |
| 708 | if type: |
| 709 | type_f, type_line = type |
| 710 | next_block, body_block = BasicBlock(), BasicBlock() |
| 711 | matches = builder.call_c(exc_matches_op, [type_f()], type_line) |
| 712 | builder.add(Branch(matches, body_block, next_block, Branch.BOOL, type_line)) |
| 713 | builder.activate_block(body_block) |
| 714 | if var: |
| 715 | target = builder.get_assignment_target(var) |
| 716 | builder.assign(target, builder.call_c(get_exc_value_op, [], var.line), var.line) |
| 717 | handler_body() |
| 718 | builder.goto(cleanup_block) |
| 719 | if next_block: |
| 720 | builder.activate_block(next_block) |
| 721 | |
| 722 | # Reraise the exception if needed |
| 723 | if next_block: |
| 724 | builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO) |
| 725 | builder.add(Unreachable()) |
| 726 |
no test coverage detected
searching dependent graphs…