mirror of https://github.com/astral-sh/ruff
Merge 8ef82e96fc into c02bd11b93
This commit is contained in:
commit
a779d92683
|
|
@ -338,7 +338,111 @@ reveal_type(a is not c) # revealed: Literal[True]
|
|||
|
||||
For tuples like `tuple[int, ...]`, `tuple[Any, ...]`
|
||||
|
||||
// TODO
|
||||
### Unsupported Comparisons
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
Comparisons between homogeneous tuples with incompatible element types should emit diagnostics for
|
||||
ordering operators (`<`, `<=`, `>`, `>=`), but not for equality operators (`==`, `!=`).
|
||||
|
||||
```py
|
||||
def f(
|
||||
a: tuple[int, ...],
|
||||
b: tuple[str, ...],
|
||||
c: tuple[str],
|
||||
):
|
||||
# Equality comparisons are always valid
|
||||
reveal_type(a == b) # revealed: bool
|
||||
reveal_type(a != b) # revealed: bool
|
||||
|
||||
# Ordering comparisons between incompatible types should emit errors
|
||||
# error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[int, ...]` and `tuple[str, ...]`"
|
||||
a < b
|
||||
# error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[str, ...]` and `tuple[int, ...]`"
|
||||
b < a
|
||||
# error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[int, ...]` and `tuple[str]`"
|
||||
a < c
|
||||
# error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[str]` and `tuple[int, ...]`"
|
||||
c < a
|
||||
```
|
||||
|
||||
When comparing fixed-length tuples with variable-length tuples, all element types that could
|
||||
potentially be compared must be compatible.
|
||||
|
||||
```py
|
||||
def _(
|
||||
var_int: tuple[int, ...],
|
||||
var_str: tuple[str, ...],
|
||||
fixed_int_str: tuple[int, str],
|
||||
):
|
||||
# Fixed `tuple[int, str]` vs. variable `tuple[int, ...]`:
|
||||
# Position 0: `int` vs. `int` are comparable.
|
||||
# Position 1 (if `var_int` has 2+ elements): `str` vs. `int` are not comparable.
|
||||
# error: [unsupported-operator]
|
||||
fixed_int_str < var_int
|
||||
|
||||
# Variable `tuple[int, ...]` vs. fixed `tuple[int, str]`:
|
||||
# Position 0: `int` vs. `int` are comparable.
|
||||
# Position 1 (if `var_int` has 2+ elements): `int` vs. `str` are not comparable.
|
||||
# error: [unsupported-operator]
|
||||
var_int < fixed_int_str
|
||||
|
||||
# Variable `tuple[str, ...]` vs. fixed `tuple[int, str]`:
|
||||
# Position 0: `str` vs. `int` are not comparable.
|
||||
# error: [unsupported-operator]
|
||||
var_str < fixed_int_str
|
||||
```
|
||||
|
||||
### Supported Comparisons
|
||||
|
||||
Comparisons between homogeneous tuples with compatible element types should work.
|
||||
|
||||
```py
|
||||
def _(a: tuple[int, ...], b: tuple[int, ...], c: tuple[bool, ...]):
|
||||
# Same element types - always valid
|
||||
reveal_type(a == b) # revealed: bool
|
||||
reveal_type(a != b) # revealed: bool
|
||||
reveal_type(a < b) # revealed: bool
|
||||
reveal_type(a <= b) # revealed: bool
|
||||
reveal_type(a > b) # revealed: bool
|
||||
reveal_type(a >= b) # revealed: bool
|
||||
|
||||
# int and bool are compatible for comparison
|
||||
reveal_type(a < c) # revealed: bool
|
||||
reveal_type(c < a) # revealed: bool
|
||||
```
|
||||
|
||||
### Tuples with Prefixes and Suffixes
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
Variable-length tuples with prefixes and suffixes are also checked.
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.11"
|
||||
```
|
||||
|
||||
```py
|
||||
def _(
|
||||
prefix_int_var_str: tuple[int, *tuple[str, ...]],
|
||||
prefix_str_var_int: tuple[str, *tuple[int, ...]],
|
||||
):
|
||||
# Prefix `int` vs. prefix `str` are not comparable.
|
||||
# error: [unsupported-operator]
|
||||
prefix_int_var_str < prefix_str_var_int
|
||||
```
|
||||
|
||||
Tuples with compatible prefixes/suffixes are allowed.
|
||||
|
||||
```py
|
||||
def _(
|
||||
prefix_int_var_int: tuple[int, *tuple[int, ...]],
|
||||
prefix_int_var_bool: tuple[int, *tuple[bool, ...]],
|
||||
):
|
||||
# Prefix `int` vs. prefix `int`, variable `int` vs. variable `bool` are all comparable.
|
||||
reveal_type(prefix_int_var_int < prefix_int_var_bool) # revealed: bool
|
||||
```
|
||||
|
||||
## Chained comparisons with elements that incorrectly implement `__bool__`
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
---
|
||||
mdtest name: tuples.md - Comparison: Tuples - Homogeneous - Tuples with Prefixes and Suffixes
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/comparison/tuples.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | def _(
|
||||
2 | prefix_int_var_str: tuple[int, *tuple[str, ...]],
|
||||
3 | prefix_str_var_int: tuple[str, *tuple[int, ...]],
|
||||
4 | ):
|
||||
5 | # Prefix `int` vs. prefix `str` are not comparable.
|
||||
6 | # error: [unsupported-operator]
|
||||
7 | prefix_int_var_str < prefix_str_var_int
|
||||
8 | def _(
|
||||
9 | prefix_int_var_int: tuple[int, *tuple[int, ...]],
|
||||
10 | prefix_int_var_bool: tuple[int, *tuple[bool, ...]],
|
||||
11 | ):
|
||||
12 | # Prefix `int` vs. prefix `int`, variable `int` vs. variable `bool` are all comparable.
|
||||
13 | reveal_type(prefix_int_var_int < prefix_int_var_bool) # revealed: bool
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[unsupported-operator]: Unsupported `<` operation
|
||||
--> src/mdtest_snippet.py:7:5
|
||||
|
|
||||
5 | # Prefix `int` vs. prefix `str` are not comparable.
|
||||
6 | # error: [unsupported-operator]
|
||||
7 | prefix_int_var_str < prefix_str_var_int
|
||||
| ------------------^^^------------------
|
||||
| | |
|
||||
| | Has type `tuple[str, *tuple[int, ...]]`
|
||||
| Has type `tuple[int, *tuple[str, ...]]`
|
||||
8 | def _(
|
||||
9 | prefix_int_var_int: tuple[int, *tuple[int, ...]],
|
||||
|
|
||||
info: Operation fails because operator `<` is not supported between objects of type `int` and `str`
|
||||
info: rule `unsupported-operator` is enabled by default
|
||||
|
||||
```
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
---
|
||||
mdtest name: tuples.md - Comparison: Tuples - Homogeneous - Unsupported Comparisons
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/comparison/tuples.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | def f(
|
||||
2 | a: tuple[int, ...],
|
||||
3 | b: tuple[str, ...],
|
||||
4 | c: tuple[str],
|
||||
5 | ):
|
||||
6 | # Equality comparisons are always valid
|
||||
7 | reveal_type(a == b) # revealed: bool
|
||||
8 | reveal_type(a != b) # revealed: bool
|
||||
9 |
|
||||
10 | # Ordering comparisons between incompatible types should emit errors
|
||||
11 | # error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[int, ...]` and `tuple[str, ...]`"
|
||||
12 | a < b
|
||||
13 | # error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[str, ...]` and `tuple[int, ...]`"
|
||||
14 | b < a
|
||||
15 | # error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[int, ...]` and `tuple[str]`"
|
||||
16 | a < c
|
||||
17 | # error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[str]` and `tuple[int, ...]`"
|
||||
18 | c < a
|
||||
19 | def _(
|
||||
20 | var_int: tuple[int, ...],
|
||||
21 | var_str: tuple[str, ...],
|
||||
22 | fixed_int_str: tuple[int, str],
|
||||
23 | ):
|
||||
24 | # Fixed `tuple[int, str]` vs. variable `tuple[int, ...]`:
|
||||
25 | # Position 0: `int` vs. `int` are comparable.
|
||||
26 | # Position 1 (if `var_int` has 2+ elements): `str` vs. `int` are not comparable.
|
||||
27 | # error: [unsupported-operator]
|
||||
28 | fixed_int_str < var_int
|
||||
29 |
|
||||
30 | # Variable `tuple[int, ...]` vs. fixed `tuple[int, str]`:
|
||||
31 | # Position 0: `int` vs. `int` are comparable.
|
||||
32 | # Position 1 (if `var_int` has 2+ elements): `int` vs. `str` are not comparable.
|
||||
33 | # error: [unsupported-operator]
|
||||
34 | var_int < fixed_int_str
|
||||
35 |
|
||||
36 | # Variable `tuple[str, ...]` vs. fixed `tuple[int, str]`:
|
||||
37 | # Position 0: `str` vs. `int` are not comparable.
|
||||
38 | # error: [unsupported-operator]
|
||||
39 | var_str < fixed_int_str
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[unsupported-operator]: Unsupported `<` operation
|
||||
--> src/mdtest_snippet.py:12:5
|
||||
|
|
||||
10 | # Ordering comparisons between incompatible types should emit errors
|
||||
11 | # error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[int, ...]` and `tuple[str, ...]`"
|
||||
12 | a < b
|
||||
| -^^^-
|
||||
| | |
|
||||
| | Has type `tuple[str, ...]`
|
||||
| Has type `tuple[int, ...]`
|
||||
13 | # error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[str, ...]` and `tuple[int, ...]`"
|
||||
14 | b < a
|
||||
|
|
||||
info: Operation fails because operator `<` is not supported between objects of type `int` and `str`
|
||||
info: rule `unsupported-operator` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[unsupported-operator]: Unsupported `<` operation
|
||||
--> src/mdtest_snippet.py:14:5
|
||||
|
|
||||
12 | a < b
|
||||
13 | # error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[str, ...]` and `tuple[int, ...]`"
|
||||
14 | b < a
|
||||
| -^^^-
|
||||
| | |
|
||||
| | Has type `tuple[int, ...]`
|
||||
| Has type `tuple[str, ...]`
|
||||
15 | # error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[int, ...]` and `tuple[str]`"
|
||||
16 | a < c
|
||||
|
|
||||
info: Operation fails because operator `<` is not supported between objects of type `str` and `int`
|
||||
info: rule `unsupported-operator` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[unsupported-operator]: Unsupported `<` operation
|
||||
--> src/mdtest_snippet.py:16:5
|
||||
|
|
||||
14 | b < a
|
||||
15 | # error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[int, ...]` and `tuple[str]`"
|
||||
16 | a < c
|
||||
| -^^^-
|
||||
| | |
|
||||
| | Has type `tuple[str]`
|
||||
| Has type `tuple[int, ...]`
|
||||
17 | # error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[str]` and `tuple[int, ...]`"
|
||||
18 | c < a
|
||||
|
|
||||
info: Operation fails because operator `<` is not supported between objects of type `int` and `str`
|
||||
info: rule `unsupported-operator` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[unsupported-operator]: Unsupported `<` operation
|
||||
--> src/mdtest_snippet.py:18:5
|
||||
|
|
||||
16 | a < c
|
||||
17 | # error: [unsupported-operator] "Operator `<` is not supported between objects of type `tuple[str]` and `tuple[int, ...]`"
|
||||
18 | c < a
|
||||
| -^^^-
|
||||
| | |
|
||||
| | Has type `tuple[int, ...]`
|
||||
| Has type `tuple[str]`
|
||||
19 | def _(
|
||||
20 | var_int: tuple[int, ...],
|
||||
|
|
||||
info: Operation fails because operator `<` is not supported between objects of type `str` and `int`
|
||||
info: rule `unsupported-operator` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[unsupported-operator]: Unsupported `<` operation
|
||||
--> src/mdtest_snippet.py:28:5
|
||||
|
|
||||
26 | # Position 1 (if `var_int` has 2+ elements): `str` vs. `int` are not comparable.
|
||||
27 | # error: [unsupported-operator]
|
||||
28 | fixed_int_str < var_int
|
||||
| -------------^^^-------
|
||||
| | |
|
||||
| | Has type `tuple[int, ...]`
|
||||
| Has type `tuple[int, str]`
|
||||
29 |
|
||||
30 | # Variable `tuple[int, ...]` vs. fixed `tuple[int, str]`:
|
||||
|
|
||||
info: Operation fails because operator `<` is not supported between objects of type `str` and `int`
|
||||
info: rule `unsupported-operator` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[unsupported-operator]: Unsupported `<` operation
|
||||
--> src/mdtest_snippet.py:34:5
|
||||
|
|
||||
32 | # Position 1 (if `var_int` has 2+ elements): `int` vs. `str` are not comparable.
|
||||
33 | # error: [unsupported-operator]
|
||||
34 | var_int < fixed_int_str
|
||||
| -------^^^-------------
|
||||
| | |
|
||||
| | Has type `tuple[int, str]`
|
||||
| Has type `tuple[int, ...]`
|
||||
35 |
|
||||
36 | # Variable `tuple[str, ...]` vs. fixed `tuple[int, str]`:
|
||||
|
|
||||
info: Operation fails because operator `<` is not supported between objects of type `int` and `str`
|
||||
info: rule `unsupported-operator` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[unsupported-operator]: Unsupported `<` operation
|
||||
--> src/mdtest_snippet.py:39:5
|
||||
|
|
||||
37 | # Position 0: `str` vs. `int` are not comparable.
|
||||
38 | # error: [unsupported-operator]
|
||||
39 | var_str < fixed_int_str
|
||||
| -------^^^-------------
|
||||
| | |
|
||||
| | Has type `tuple[int, str]`
|
||||
| Has type `tuple[str, ...]`
|
||||
|
|
||||
info: Operation fails because operator `<` is not supported between objects of type `str` and `int`
|
||||
info: rule `unsupported-operator` is enabled by default
|
||||
|
||||
```
|
||||
|
|
@ -11191,84 +11191,300 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
range: TextRange,
|
||||
visitor: &BinaryComparisonVisitor<'db>,
|
||||
) -> Result<Type<'db>, UnsupportedComparisonError<'db>> {
|
||||
// If either tuple is variable length, we can make no assumptions about the relative
|
||||
// lengths of the tuples, and therefore neither about how they compare lexicographically.
|
||||
// TODO: Consider comparing the prefixes of the tuples, since that could give a comparison
|
||||
// result regardless of how long the variable-length tuple is.
|
||||
let (TupleSpec::Fixed(left), TupleSpec::Fixed(right)) = (left, right) else {
|
||||
return Ok(Type::unknown());
|
||||
};
|
||||
match (left, right) {
|
||||
// Both fixed-length: perform full lexicographic comparison.
|
||||
(TupleSpec::Fixed(left), TupleSpec::Fixed(right)) => {
|
||||
let left_iter = left.elements().copied();
|
||||
let right_iter = right.elements().copied();
|
||||
|
||||
let left_iter = left.elements().copied();
|
||||
let right_iter = right.elements().copied();
|
||||
let mut builder = UnionBuilder::new(self.db());
|
||||
|
||||
let mut builder = UnionBuilder::new(self.db());
|
||||
for (l_ty, r_ty) in left_iter.zip(right_iter) {
|
||||
let pairwise_eq_result = self
|
||||
.infer_binary_type_comparison(l_ty, ast::CmpOp::Eq, r_ty, range, visitor)
|
||||
.expect(
|
||||
"infer_binary_type_comparison should never return None for `CmpOp::Eq`",
|
||||
);
|
||||
|
||||
for (l_ty, r_ty) in left_iter.zip(right_iter) {
|
||||
let pairwise_eq_result = self
|
||||
.infer_binary_type_comparison(l_ty, ast::CmpOp::Eq, r_ty, range, visitor)
|
||||
.expect("infer_binary_type_comparison should never return None for `CmpOp::Eq`");
|
||||
match pairwise_eq_result
|
||||
.try_bool(self.db())
|
||||
.unwrap_or_else(|err| {
|
||||
// TODO: We should, whenever possible, pass the range of the left and right elements
|
||||
// instead of the range of the whole tuple.
|
||||
err.report_diagnostic(&self.context, range);
|
||||
err.fallback_truthiness()
|
||||
}) {
|
||||
// - AlwaysTrue : Continue to the next pair for lexicographic comparison
|
||||
Truthiness::AlwaysTrue => continue,
|
||||
// - AlwaysFalse:
|
||||
// Lexicographic comparisons will always terminate with this pair.
|
||||
// Complete the comparison and return the result.
|
||||
// - Ambiguous:
|
||||
// Lexicographic comparisons might continue to the next pair (if eq_result is true),
|
||||
// or terminate here (if eq_result is false).
|
||||
// To account for cases where the comparison terminates here, add the pairwise comparison result to the union builder.
|
||||
eq_truthiness @ (Truthiness::AlwaysFalse | Truthiness::Ambiguous) => {
|
||||
let pairwise_compare_result = match op {
|
||||
RichCompareOperator::Lt
|
||||
| RichCompareOperator::Le
|
||||
| RichCompareOperator::Gt
|
||||
| RichCompareOperator::Ge => self.infer_binary_type_comparison(
|
||||
l_ty,
|
||||
op.into(),
|
||||
r_ty,
|
||||
range,
|
||||
visitor,
|
||||
)?,
|
||||
// For `==` and `!=`, we already figure out the result from `pairwise_eq_result`
|
||||
// NOTE: The CPython implementation does not account for non-boolean return types
|
||||
// or cases where `!=` is not the negation of `==`, we also do not consider these cases.
|
||||
RichCompareOperator::Eq => Type::BooleanLiteral(false),
|
||||
RichCompareOperator::Ne => Type::BooleanLiteral(true),
|
||||
};
|
||||
|
||||
match pairwise_eq_result
|
||||
.try_bool(self.db())
|
||||
.unwrap_or_else(|err| {
|
||||
// TODO: We should, whenever possible, pass the range of the left and right elements
|
||||
// instead of the range of the whole tuple.
|
||||
err.report_diagnostic(&self.context, range);
|
||||
err.fallback_truthiness()
|
||||
}) {
|
||||
// - AlwaysTrue : Continue to the next pair for lexicographic comparison
|
||||
Truthiness::AlwaysTrue => continue,
|
||||
// - AlwaysFalse:
|
||||
// Lexicographic comparisons will always terminate with this pair.
|
||||
// Complete the comparison and return the result.
|
||||
// - Ambiguous:
|
||||
// Lexicographic comparisons might continue to the next pair (if eq_result is true),
|
||||
// or terminate here (if eq_result is false).
|
||||
// To account for cases where the comparison terminates here, add the pairwise comparison result to the union builder.
|
||||
eq_truthiness @ (Truthiness::AlwaysFalse | Truthiness::Ambiguous) => {
|
||||
let pairwise_compare_result = match op {
|
||||
RichCompareOperator::Lt
|
||||
| RichCompareOperator::Le
|
||||
| RichCompareOperator::Gt
|
||||
| RichCompareOperator::Ge => self.infer_binary_type_comparison(
|
||||
l_ty,
|
||||
op.into(),
|
||||
r_ty,
|
||||
range,
|
||||
visitor,
|
||||
)?,
|
||||
// For `==` and `!=`, we already figure out the result from `pairwise_eq_result`
|
||||
// NOTE: The CPython implementation does not account for non-boolean return types
|
||||
// or cases where `!=` is not the negation of `==`, we also do not consider these cases.
|
||||
RichCompareOperator::Eq => Type::BooleanLiteral(false),
|
||||
RichCompareOperator::Ne => Type::BooleanLiteral(true),
|
||||
};
|
||||
builder = builder.add(pairwise_compare_result);
|
||||
|
||||
builder = builder.add(pairwise_compare_result);
|
||||
if eq_truthiness.is_ambiguous() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if eq_truthiness.is_ambiguous() {
|
||||
continue;
|
||||
return Ok(builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(builder.build());
|
||||
}
|
||||
|
||||
// if no more items to compare, we just compare sizes
|
||||
let (left_len, right_len) = (left.len(), right.len());
|
||||
|
||||
builder = builder.add(Type::BooleanLiteral(match op {
|
||||
RichCompareOperator::Eq => left_len == right_len,
|
||||
RichCompareOperator::Ne => left_len != right_len,
|
||||
RichCompareOperator::Lt => left_len < right_len,
|
||||
RichCompareOperator::Le => left_len <= right_len,
|
||||
RichCompareOperator::Gt => left_len > right_len,
|
||||
RichCompareOperator::Ge => left_len >= right_len,
|
||||
}));
|
||||
|
||||
Ok(builder.build())
|
||||
}
|
||||
|
||||
// At least one tuple is variable-length. We can make no assumptions about
|
||||
// the relative lengths of the tuples, and therefore neither about how they
|
||||
// compare lexicographically. However, we still need to verify that the
|
||||
// element types are comparable for ordering comparisons.
|
||||
|
||||
// For equality comparisons (==, !=), any two objects can be compared,
|
||||
// and tuple equality always returns bool regardless of element __eq__ return types.
|
||||
(TupleSpec::Variable(_), _) | (_, TupleSpec::Variable(_))
|
||||
if matches!(op, RichCompareOperator::Eq | RichCompareOperator::Ne) =>
|
||||
{
|
||||
Ok(KnownClass::Bool.to_instance(self.db()))
|
||||
}
|
||||
|
||||
// Both variable: check all elements that could potentially be compared.
|
||||
(TupleSpec::Variable(left_var), TupleSpec::Variable(right_var)) => {
|
||||
let mut builder = UnionBuilder::new(self.db());
|
||||
|
||||
// 1. Compare prefix elements at matching positions.
|
||||
for (l_el, r_el) in left_var.prefix_elements().zip(right_var.prefix_elements()) {
|
||||
builder = builder.add(self.infer_binary_type_comparison(
|
||||
*l_el,
|
||||
op.into(),
|
||||
*r_el,
|
||||
range,
|
||||
visitor,
|
||||
)?);
|
||||
}
|
||||
|
||||
// 2. Left's extra prefix elements are compared with right's variable.
|
||||
for l_el in left_var.prefix_elements().skip(right_var.prefix.len()) {
|
||||
builder = builder.add(self.infer_binary_type_comparison(
|
||||
*l_el,
|
||||
op.into(),
|
||||
right_var.variable,
|
||||
range,
|
||||
visitor,
|
||||
)?);
|
||||
}
|
||||
|
||||
// 3. Right's extra prefix elements are compared with left's variable.
|
||||
for r_el in right_var.prefix_elements().skip(left_var.prefix.len()) {
|
||||
builder = builder.add(self.infer_binary_type_comparison(
|
||||
left_var.variable,
|
||||
op.into(),
|
||||
*r_el,
|
||||
range,
|
||||
visitor,
|
||||
)?);
|
||||
}
|
||||
|
||||
// 4. Variable elements can be compared at any overlapping position.
|
||||
builder = builder.add(self.infer_binary_type_comparison(
|
||||
left_var.variable,
|
||||
op.into(),
|
||||
right_var.variable,
|
||||
range,
|
||||
visitor,
|
||||
)?);
|
||||
|
||||
// 5. Left's extra suffix elements are compared with right's variable.
|
||||
for l_el in left_var
|
||||
.suffix_elements()
|
||||
.rev()
|
||||
.skip(right_var.suffix.len())
|
||||
{
|
||||
builder = builder.add(self.infer_binary_type_comparison(
|
||||
*l_el,
|
||||
op.into(),
|
||||
right_var.variable,
|
||||
range,
|
||||
visitor,
|
||||
)?);
|
||||
}
|
||||
|
||||
// 6. Right's extra suffix elements are compared with left's variable.
|
||||
for r_el in right_var
|
||||
.suffix_elements()
|
||||
.rev()
|
||||
.skip(left_var.suffix.len())
|
||||
{
|
||||
builder = builder.add(self.infer_binary_type_comparison(
|
||||
left_var.variable,
|
||||
op.into(),
|
||||
*r_el,
|
||||
range,
|
||||
visitor,
|
||||
)?);
|
||||
}
|
||||
|
||||
// 7. Compare suffix elements at matching positions (from the end).
|
||||
for (l_el, r_el) in left_var
|
||||
.suffix_elements()
|
||||
.rev()
|
||||
.zip(right_var.suffix_elements().rev())
|
||||
{
|
||||
builder = builder.add(self.infer_binary_type_comparison(
|
||||
*l_el,
|
||||
op.into(),
|
||||
*r_el,
|
||||
range,
|
||||
visitor,
|
||||
)?);
|
||||
}
|
||||
|
||||
// Length comparison (when all elements are equal) returns bool.
|
||||
builder = builder.add(KnownClass::Bool.to_instance(self.db()));
|
||||
|
||||
Ok(builder.build())
|
||||
}
|
||||
|
||||
// Left variable, right fixed: check which elements could be compared.
|
||||
(TupleSpec::Variable(left_var), TupleSpec::Fixed(right_fixed)) => {
|
||||
let mut builder = UnionBuilder::new(self.db());
|
||||
|
||||
// Compare left's prefix with right's corresponding elements.
|
||||
for (l_el, r_el) in left_var.prefix_elements().zip(right_fixed.elements()) {
|
||||
builder = builder.add(self.infer_binary_type_comparison(
|
||||
*l_el,
|
||||
op.into(),
|
||||
*r_el,
|
||||
range,
|
||||
visitor,
|
||||
)?);
|
||||
}
|
||||
|
||||
// Compare left's suffix with right's corresponding elements (from end).
|
||||
for (l_el, r_el) in left_var
|
||||
.suffix_elements()
|
||||
.rev()
|
||||
.zip(right_fixed.elements().rev())
|
||||
{
|
||||
builder = builder.add(self.infer_binary_type_comparison(
|
||||
*l_el,
|
||||
op.into(),
|
||||
*r_el,
|
||||
range,
|
||||
visitor,
|
||||
)?);
|
||||
}
|
||||
|
||||
// Compare left's variable with right's "middle" elements
|
||||
// (those not covered by prefix or suffix).
|
||||
let middle_start = left_var.prefix.len();
|
||||
let middle_end = right_fixed.len().saturating_sub(left_var.suffix.len());
|
||||
for r_el in right_fixed
|
||||
.elements()
|
||||
.skip(middle_start)
|
||||
.take(middle_end.saturating_sub(middle_start))
|
||||
{
|
||||
builder = builder.add(self.infer_binary_type_comparison(
|
||||
left_var.variable,
|
||||
op.into(),
|
||||
*r_el,
|
||||
range,
|
||||
visitor,
|
||||
)?);
|
||||
}
|
||||
|
||||
// Length comparison (when all elements are equal) returns bool.
|
||||
builder = builder.add(KnownClass::Bool.to_instance(self.db()));
|
||||
|
||||
Ok(builder.build())
|
||||
}
|
||||
|
||||
// Left fixed, right variable: check which elements could be compared.
|
||||
(TupleSpec::Fixed(left_fixed), TupleSpec::Variable(right_var)) => {
|
||||
let mut builder = UnionBuilder::new(self.db());
|
||||
|
||||
// Compare left's elements with right's prefix.
|
||||
for (l_el, r_el) in left_fixed.elements().zip(right_var.prefix_elements()) {
|
||||
builder = builder.add(self.infer_binary_type_comparison(
|
||||
*l_el,
|
||||
op.into(),
|
||||
*r_el,
|
||||
range,
|
||||
visitor,
|
||||
)?);
|
||||
}
|
||||
|
||||
// Compare left's elements (from end) with right's suffix.
|
||||
for (l_el, r_el) in left_fixed
|
||||
.elements()
|
||||
.rev()
|
||||
.zip(right_var.suffix_elements().rev())
|
||||
{
|
||||
builder = builder.add(self.infer_binary_type_comparison(
|
||||
*l_el,
|
||||
op.into(),
|
||||
*r_el,
|
||||
range,
|
||||
visitor,
|
||||
)?);
|
||||
}
|
||||
|
||||
// Compare left's "middle" elements with right's variable.
|
||||
let middle_start = right_var.prefix.len();
|
||||
let middle_end = left_fixed.len().saturating_sub(right_var.suffix.len());
|
||||
for l_el in left_fixed
|
||||
.elements()
|
||||
.skip(middle_start)
|
||||
.take(middle_end.saturating_sub(middle_start))
|
||||
{
|
||||
builder = builder.add(self.infer_binary_type_comparison(
|
||||
*l_el,
|
||||
op.into(),
|
||||
right_var.variable,
|
||||
range,
|
||||
visitor,
|
||||
)?);
|
||||
}
|
||||
|
||||
// Length comparison (when all elements are equal) returns bool.
|
||||
builder = builder.add(KnownClass::Bool.to_instance(self.db()));
|
||||
|
||||
Ok(builder.build())
|
||||
}
|
||||
}
|
||||
|
||||
// if no more items to compare, we just compare sizes
|
||||
let (left_len, right_len) = (left.len(), right.len());
|
||||
|
||||
builder = builder.add(Type::BooleanLiteral(match op {
|
||||
RichCompareOperator::Eq => left_len == right_len,
|
||||
RichCompareOperator::Ne => left_len != right_len,
|
||||
RichCompareOperator::Lt => left_len < right_len,
|
||||
RichCompareOperator::Le => left_len <= right_len,
|
||||
RichCompareOperator::Gt => left_len > right_len,
|
||||
RichCompareOperator::Ge => left_len >= right_len,
|
||||
}));
|
||||
|
||||
Ok(builder.build())
|
||||
}
|
||||
|
||||
fn infer_subscript_expression(&mut self, subscript: &ast::ExprSubscript) -> Type<'db> {
|
||||
|
|
|
|||
Loading…
Reference in New Issue