Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 44 additions & 5 deletions mypyc/irbuild/ll_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@
c_pyssize_t_rprimitive,
c_size_t_rprimitive,
check_native_int_range,
dict_rprimitive,
float_rprimitive,
int_rprimitive,
is_bool_or_bit_rprimitive,
Expand All @@ -110,13 +109,13 @@
is_list_rprimitive,
is_none_rprimitive,
is_object_rprimitive,
is_optional_type,
is_set_rprimitive,
is_short_int_rprimitive,
is_str_rprimitive,
is_tagged,
is_tuple_rprimitive,
is_uint8_rprimitive,
list_rprimitive,
none_rprimitive,
object_pointer_rprimitive,
object_rprimitive,
Expand Down Expand Up @@ -1684,6 +1683,44 @@ def unary_not(self, value: Value, line: int, *, likely_bool: bool = False) -> Va
if is_bool_or_bit_rprimitive(typ):
mask = Integer(1, typ, line)
return self.int_op(typ, value, mask, IntOp.XOR, line)
if is_tagged(typ) or is_fixed_width_rtype(typ):
return self.binary_op(value, Integer(0), "==", line)
if (
is_str_rprimitive(typ)
or is_list_rprimitive(typ)
or is_tuple_rprimitive(typ)
or is_dict_rprimitive(typ)
or isinstance(typ, RInstance)
):
bool_val = self.bool_value(value)
return self.unary_not(bool_val, line)
if is_optional_type(typ):
value_typ = optional_value_type(typ)
assert value_typ
if (
is_str_rprimitive(value_typ)
or is_list_rprimitive(value_typ)
or is_tuple_rprimitive(value_typ)
or is_dict_rprimitive(value_typ)
or isinstance(value_typ, RInstance)
):
# 'X | None' type: Check for None first and then specialize for X.
res = Register(bit_rprimitive)
cmp = self.add(ComparisonOp(value, self.none_object(), ComparisonOp.EQ, line))
none, not_none, out = BasicBlock(), BasicBlock(), BasicBlock()
self.add(Branch(cmp, none, not_none, Branch.BOOL))
self.activate_block(none)
self.add(Assign(res, self.true()))
self.goto(out)
self.activate_block(not_none)
val = self.unary_not(
self.unbox_or_cast(value, value_typ, line, can_borrow=True, unchecked=True),
line,
)
self.add(Assign(res, val))
self.goto(out)
self.activate_block(out)
return res
if likely_bool and is_object_rprimitive(typ):
# First quickly check if it's a bool, and otherwise fall back to generic op.
res = Register(bit_rprimitive)
Expand Down Expand Up @@ -1882,10 +1919,12 @@ def bool_value(self, value: Value) -> Value:
elif is_fixed_width_rtype(value.type):
zero = Integer(0, value.type)
result = self.add(ComparisonOp(value, zero, ComparisonOp.NEQ))
elif is_same_type(value.type, str_rprimitive):
elif is_str_rprimitive(value.type):
result = self.call_c(str_check_if_true, [value], value.line)
elif is_same_type(value.type, list_rprimitive) or is_same_type(
value.type, dict_rprimitive
elif (
is_list_rprimitive(value.type)
or is_dict_rprimitive(value.type)
or is_tuple_rprimitive(value.type)
):
length = self.builtin_len(value, value.line)
zero = Integer(0)
Expand Down
178 changes: 178 additions & 0 deletions mypyc/test-data/irbuild-bool.test
Original file line number Diff line number Diff line change
Expand Up @@ -473,3 +473,181 @@ L0:
r0 = x == y
r1 = r0 ^ 1
return r1

[case testUnaryNotWithPrimitiveTypes]
def not_obj(x: object) -> bool:
return not x

def not_int(x: int) -> bool:
return not x

def not_str(x: str) -> bool:
return not x

def not_list(x: list[int]) -> bool:
return not x

def not_tuple(x: tuple[int, ...]) -> bool:
return not x

def not_dict(x: dict[str, int]) -> bool:
return not x
[out]
def not_obj(x):
x :: object
r0 :: i32
r1 :: bit
r2 :: bool
L0:
r0 = PyObject_Not(x)
r1 = r0 >= 0 :: signed
r2 = truncate r0: i32 to builtins.bool
return r2
def not_int(x):
x :: int
r0 :: bit
L0:
r0 = int_eq x, 0
return r0
def not_str(x):
x :: str
r0, r1 :: bit
L0:
r0 = CPyStr_IsTrue(x)
r1 = r0 ^ 1
return r1
def not_list(x):
x :: list
r0 :: native_int
r1 :: short_int
r2, r3 :: bit
L0:
r0 = var_object_size x
r1 = r0 << 1
r2 = int_ne r1, 0
r3 = r2 ^ 1
return r3
def not_tuple(x):
x :: tuple
r0 :: native_int
r1 :: short_int
r2, r3 :: bit
L0:
r0 = var_object_size x
r1 = r0 << 1
r2 = int_ne r1, 0
r3 = r2 ^ 1
return r3
def not_dict(x):
x :: dict
r0 :: native_int
r1 :: short_int
r2, r3 :: bit
L0:
r0 = PyDict_Size(x)
r1 = r0 << 1
r2 = int_ne r1, 0
r3 = r2 ^ 1
return r3

[case testUnaryNotWithNativeClass]
from __future__ import annotations

class C:
def __bool__(self) -> bool:
return True

def not_c(x: C) -> bool:
return not x

def not_c_opt(x: C | None) -> bool:
return not x
[out]
def C.__bool__(self):
self :: __main__.C
L0:
return 1
def not_c(x):
x :: __main__.C
r0, r1 :: bool
L0:
r0 = x.__bool__()
r1 = r0 ^ 1
return r1
def not_c_opt(x):
x :: union[__main__.C, None]
r0 :: object
r1, r2 :: bit
r3 :: __main__.C
r4, r5 :: bool
L0:
r0 = load_address _Py_NoneStruct
r1 = x == r0
if r1 goto L1 else goto L2 :: bool
L1:
r2 = 1
goto L3
L2:
r3 = unchecked borrow cast(__main__.C, x)
r4 = r3.__bool__()
r5 = r4 ^ 1
r2 = r5
L3:
keep_alive x
return r2

[case testUnaryNotWithOptionalPrimitiveTypes]
from __future__ import annotations

def not_str(x: str | None) -> bool:
return not x

def not_list(x: list[int] | None) -> bool:
return not x
[out]
def not_str(x):
x :: union[str, None]
r0 :: object
r1, r2 :: bit
r3 :: str
r4, r5 :: bit
L0:
r0 = load_address _Py_NoneStruct
r1 = x == r0
if r1 goto L1 else goto L2 :: bool
L1:
r2 = 1
goto L3
L2:
r3 = unchecked borrow cast(str, x)
r4 = CPyStr_IsTrue(r3)
r5 = r4 ^ 1
r2 = r5
L3:
keep_alive x
return r2
def not_list(x):
x :: union[list, None]
r0 :: object
r1, r2 :: bit
r3 :: list
r4 :: native_int
r5 :: short_int
r6, r7 :: bit
L0:
r0 = load_address _Py_NoneStruct
r1 = x == r0
if r1 goto L1 else goto L2 :: bool
L1:
r2 = 1
goto L3
L2:
r3 = unchecked borrow cast(list, x)
r4 = var_object_size r3
r5 = r4 << 1
r6 = int_ne r5, 0
r7 = r6 ^ 1
r2 = r7
L3:
keep_alive x
return r2
8 changes: 8 additions & 0 deletions mypyc/test-data/irbuild-i64.test
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,8 @@ def f(x: i64) -> i64:
elif not x:
return 6
return 3
def unary_not(x: i64) -> bool:
return not x
[out]
def f(x):
x :: i64
Expand All @@ -964,6 +966,12 @@ L3:
L4:
L5:
return 3
def unary_not(x):
x :: i64
r0 :: bit
L0:
r0 = x == 0
return r0

[case testI64AssignMixed_64bit]
from mypy_extensions import i64
Expand Down
Loading
Loading