|
14 | 14 |
|
15 | 15 | from __future__ import annotations
|
16 | 16 |
|
17 |
| -from typing import Callable, Final, Optional |
| 17 | +from typing import Callable, Final, Optional, cast |
18 | 18 |
|
19 | 19 | from mypy.nodes import (
|
20 | 20 | ARG_NAMED,
|
|
40 | 40 | Call,
|
41 | 41 | Extend,
|
42 | 42 | Integer,
|
| 43 | + PrimitiveDescription, |
43 | 44 | RaiseStandardError,
|
44 | 45 | Register,
|
45 | 46 | SetAttr,
|
@@ -589,26 +590,81 @@ def translate_isinstance(builder: IRBuilder, expr: CallExpr, callee: RefExpr) ->
|
589 | 590 | if not (len(expr.args) == 2 and expr.arg_kinds == [ARG_POS, ARG_POS]):
|
590 | 591 | return None
|
591 | 592 |
|
592 |
| - if isinstance(expr.args[1], (RefExpr, TupleExpr)): |
593 |
| - builder.types[expr.args[0]] = AnyType(TypeOfAny.from_error) |
| 593 | + obj_expr = expr.args[0] |
| 594 | + type_expr = expr.args[1] |
594 | 595 |
|
595 |
| - irs = builder.flatten_classes(expr.args[1]) |
| 596 | + if isinstance(type_expr, TupleExpr) and not type_expr.items: |
| 597 | + # we can compile this case to a noop |
| 598 | + return builder.false() |
| 599 | + |
| 600 | + if isinstance(type_expr, (RefExpr, TupleExpr)): |
| 601 | + builder.types[obj_expr] = AnyType(TypeOfAny.from_error) |
| 602 | + |
| 603 | + irs = builder.flatten_classes(type_expr) |
596 | 604 | if irs is not None:
|
597 | 605 | can_borrow = all(
|
598 | 606 | ir.is_ext_class and not ir.inherits_python and not ir.allow_interpreted_subclasses
|
599 | 607 | for ir in irs
|
600 | 608 | )
|
601 |
| - obj = builder.accept(expr.args[0], can_borrow=can_borrow) |
| 609 | + obj = builder.accept(obj_expr, can_borrow=can_borrow) |
602 | 610 | return builder.builder.isinstance_helper(obj, irs, expr.line)
|
603 | 611 |
|
604 |
| - if isinstance(expr.args[1], RefExpr): |
605 |
| - node = expr.args[1].node |
| 612 | + if isinstance(type_expr, RefExpr): |
| 613 | + node = type_expr.node |
606 | 614 | if node:
|
607 | 615 | desc = isinstance_primitives.get(node.fullname)
|
608 | 616 | if desc:
|
609 |
| - obj = builder.accept(expr.args[0]) |
| 617 | + obj = builder.accept(obj_expr) |
610 | 618 | return builder.primitive_op(desc, [obj], expr.line)
|
611 | 619 |
|
| 620 | + elif isinstance(type_expr, TupleExpr): |
| 621 | + node_names: list[str] = [] |
| 622 | + for item in type_expr.items: |
| 623 | + if not isinstance(item, RefExpr): |
| 624 | + return None |
| 625 | + if item.node is None: |
| 626 | + return None |
| 627 | + if item.node.fullname not in node_names: |
| 628 | + node_names.append(item.node.fullname) |
| 629 | + |
| 630 | + descs = [isinstance_primitives.get(fullname) for fullname in node_names] |
| 631 | + if None in descs: |
| 632 | + # not all types are primitive types, abort |
| 633 | + return None |
| 634 | + |
| 635 | + obj = builder.accept(obj_expr) |
| 636 | + |
| 637 | + retval = Register(bool_rprimitive) |
| 638 | + pass_block = BasicBlock() |
| 639 | + fail_block = BasicBlock() |
| 640 | + exit_block = BasicBlock() |
| 641 | + |
| 642 | + # Chain the checks: if any succeed, jump to pass_block; else, continue |
| 643 | + for i, desc in enumerate(descs): |
| 644 | + is_last = i == len(descs) - 1 |
| 645 | + next_block = fail_block if is_last else BasicBlock() |
| 646 | + builder.add_bool_branch( |
| 647 | + builder.primitive_op(cast(PrimitiveDescription, desc), [obj], expr.line), |
| 648 | + pass_block, |
| 649 | + next_block, |
| 650 | + ) |
| 651 | + if not is_last: |
| 652 | + builder.activate_block(next_block) |
| 653 | + |
| 654 | + # If any check passed |
| 655 | + builder.activate_block(pass_block) |
| 656 | + builder.assign(retval, builder.true(), expr.line) |
| 657 | + builder.goto(exit_block) |
| 658 | + |
| 659 | + # If all checks failed |
| 660 | + builder.activate_block(fail_block) |
| 661 | + builder.assign(retval, builder.false(), expr.line) |
| 662 | + builder.goto(exit_block) |
| 663 | + |
| 664 | + # Return the result |
| 665 | + builder.activate_block(exit_block) |
| 666 | + return retval |
| 667 | + |
612 | 668 | return None
|
613 | 669 |
|
614 | 670 |
|
|
0 commit comments