-
-
Notifications
You must be signed in to change notification settings - Fork 3k
[mypyc] feat: specialize isinstance for tuple of primitive types #19949
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1975746
e23d1f1
95ee648
73f6297
7d5b66a
ceda7cc
628294d
0d51ead
3ea6d17
8a3a1b2
355ceec
c29d5cd
4e1596c
fa7f46b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,7 @@ | |
|
||
from __future__ import annotations | ||
|
||
from typing import Callable, Final, Optional | ||
from typing import Callable, Final, Optional, cast | ||
|
||
from mypy.nodes import ( | ||
ARG_NAMED, | ||
|
@@ -40,6 +40,7 @@ | |
Call, | ||
Extend, | ||
Integer, | ||
PrimitiveDescription, | ||
RaiseStandardError, | ||
Register, | ||
Truncate, | ||
|
@@ -587,26 +588,81 @@ def translate_isinstance(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> | |
if not (len(expr.args) == 2 and expr.arg_kinds == [ARG_POS, ARG_POS]): | ||
return None | ||
|
||
if isinstance(expr.args[1], (RefExpr, TupleExpr)): | ||
builder.types[expr.args[0]] = AnyType(TypeOfAny.from_error) | ||
obj_expr = expr.args[0] | ||
type_expr = expr.args[1] | ||
|
||
irs = builder.flatten_classes(expr.args[1]) | ||
if isinstance(type_expr, TupleExpr) and not type_expr.items: | ||
# we can compile this case to a noop | ||
return builder.false() | ||
|
||
if isinstance(type_expr, (RefExpr, TupleExpr)): | ||
builder.types[obj_expr] = AnyType(TypeOfAny.from_error) | ||
|
||
irs = builder.flatten_classes(type_expr) | ||
if irs is not None: | ||
can_borrow = all( | ||
ir.is_ext_class and not ir.inherits_python and not ir.allow_interpreted_subclasses | ||
for ir in irs | ||
) | ||
obj = builder.accept(expr.args[0], can_borrow=can_borrow) | ||
obj = builder.accept(obj_expr, can_borrow=can_borrow) | ||
return builder.builder.isinstance_helper(obj, irs, expr.line) | ||
|
||
if isinstance(expr.args[1], RefExpr): | ||
node = expr.args[1].node | ||
if isinstance(type_expr, RefExpr): | ||
node = type_expr.node | ||
if node: | ||
desc = isinstance_primitives.get(node.fullname) | ||
if desc: | ||
obj = builder.accept(expr.args[0]) | ||
obj = builder.accept(obj_expr) | ||
return builder.primitive_op(desc, [obj], expr.line) | ||
|
||
elif isinstance(type_expr, TupleExpr): | ||
node_names: list[str] = [] | ||
for item in type_expr.items: | ||
if not isinstance(item, RefExpr): | ||
return None | ||
if item.node is None: | ||
return None | ||
if item.node.fullname not in node_names: | ||
node_names.append(item.node.fullname) | ||
|
||
descs = [isinstance_primitives.get(fullname) for fullname in node_names] | ||
if None in descs: | ||
# not all types are primitive types, abort | ||
return None | ||
|
||
obj = builder.accept(obj_expr) | ||
|
||
retval = Register(bool_rprimitive) | ||
pass_block = BasicBlock() | ||
fail_block = BasicBlock() | ||
exit_block = BasicBlock() | ||
|
||
# Chain the checks: if any succeed, jump to pass_block; else, continue | ||
for i, desc in enumerate(descs): | ||
is_last = i == len(descs) - 1 | ||
next_block = fail_block if is_last else BasicBlock() | ||
builder.add_bool_branch( | ||
builder.primitive_op(cast(PrimitiveDescription, desc), [obj], expr.line), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here a slightly better way to narrow the type would be to use |
||
pass_block, | ||
next_block, | ||
) | ||
if not is_last: | ||
builder.activate_block(next_block) | ||
|
||
# If any check passed | ||
builder.activate_block(pass_block) | ||
builder.assign(retval, builder.true(), expr.line) | ||
builder.goto(exit_block) | ||
|
||
# If all checks failed | ||
builder.activate_block(fail_block) | ||
builder.assign(retval, builder.false(), expr.line) | ||
builder.goto(exit_block) | ||
|
||
# Return the result | ||
builder.activate_block(exit_block) | ||
return retval | ||
|
||
return None | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -189,3 +189,31 @@ def is_tuple(x): | |
L0: | ||
r0 = PyTuple_Check(x) | ||
return r0 | ||
|
||
[case testTupleOfPrimitives] | ||
from typing import Any | ||
|
||
def is_instance(x: Any) -> bool: | ||
return isinstance(x, (str, int, bytes)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test also single-item tuple as an edge case. |
||
|
||
[out] | ||
def is_instance(x): | ||
x :: object | ||
r0, r1, r2 :: bit | ||
r3 :: bool | ||
L0: | ||
r0 = PyUnicode_Check(x) | ||
if r0 goto L3 else goto L1 :: bool | ||
L1: | ||
r1 = PyLong_Check(x) | ||
if r1 goto L3 else goto L2 :: bool | ||
L2: | ||
r2 = PyBytes_Check(x) | ||
if r2 goto L3 else goto L4 :: bool | ||
L3: | ||
r3 = 1 | ||
goto L5 | ||
L4: | ||
r3 = 0 | ||
L5: | ||
return r3 |
Uh oh!
There was an error while loading. Please reload this page.