Compare two strings
(self, lhs: Value, rhs: Value, op: str, line: int)
| 1727 | return check |
| 1728 | |
| 1729 | def compare_strings(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: |
| 1730 | """Compare two strings""" |
| 1731 | if op == "==": |
| 1732 | # We can specialize this case if one or both values are string literals |
| 1733 | literal_fastpath = False |
| 1734 | |
| 1735 | def is_string_literal(value: Value) -> TypeGuard[LoadLiteral]: |
| 1736 | return isinstance(value, LoadLiteral) and is_str_rprimitive(value.type) |
| 1737 | |
| 1738 | if is_string_literal(lhs): |
| 1739 | if is_string_literal(rhs): |
| 1740 | # we can optimize out the check entirely in some constant-folded cases |
| 1741 | return self.true() if lhs.value == rhs.value else self.false() |
| 1742 | |
| 1743 | # if lhs argument is string literal, switch sides to match specializer C api |
| 1744 | lhs, rhs = rhs, lhs |
| 1745 | literal_fastpath = True |
| 1746 | elif is_string_literal(rhs): |
| 1747 | literal_fastpath = True |
| 1748 | |
| 1749 | if literal_fastpath: |
| 1750 | literal_string = cast(str, cast(LoadLiteral, rhs).value) |
| 1751 | literal_length = Integer(len(literal_string), c_pyssize_t_rprimitive, line) |
| 1752 | return self.primitive_op(str_eq_literal, [lhs, rhs, literal_length], line) |
| 1753 | |
| 1754 | return self.primitive_op(str_eq, [lhs, rhs], line) |
| 1755 | |
| 1756 | elif op == "!=": |
| 1757 | # perform a standard equality check, then negate |
| 1758 | eq = self.compare_strings(lhs, rhs, "==", line) |
| 1759 | return self.add(ComparisonOp(eq, self.false(), ComparisonOp.EQ, line)) |
| 1760 | |
| 1761 | # TODO: modify 'str' to use same interface as 'compare_bytes' as it would avoid |
| 1762 | # call to PyErr_Occurred() below |
| 1763 | |
| 1764 | compare_result = self.call_c(unicode_compare, [lhs, rhs], line) |
| 1765 | error_constant = Integer(-1, c_int_rprimitive, line) |
| 1766 | compare_error_check = self.add( |
| 1767 | ComparisonOp(compare_result, error_constant, ComparisonOp.EQ, line) |
| 1768 | ) |
| 1769 | exception_check, propagate, final_compare = BasicBlock(), BasicBlock(), BasicBlock() |
| 1770 | branch = Branch(compare_error_check, exception_check, final_compare, Branch.BOOL) |
| 1771 | branch.negated = False |
| 1772 | self.add(branch) |
| 1773 | self.activate_block(exception_check) |
| 1774 | check_error_result = self.call_c(err_occurred_op, [], line) |
| 1775 | null = Integer(0, pointer_rprimitive, line) |
| 1776 | compare_error_check = self.add( |
| 1777 | ComparisonOp(check_error_result, null, ComparisonOp.NEQ, line) |
| 1778 | ) |
| 1779 | branch = Branch(compare_error_check, propagate, final_compare, Branch.BOOL) |
| 1780 | branch.negated = False |
| 1781 | self.add(branch) |
| 1782 | self.activate_block(propagate) |
| 1783 | self.call_c(keep_propagating_op, [], line) |
| 1784 | self.goto(final_compare) |
| 1785 | self.activate_block(final_compare) |
| 1786 | op_type = ComparisonOp.EQ if op == "==" else ComparisonOp.NEQ |
no test coverage detected