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, ...]`
|
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__`
|
## 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,14 +11191,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
range: TextRange,
|
range: TextRange,
|
||||||
visitor: &BinaryComparisonVisitor<'db>,
|
visitor: &BinaryComparisonVisitor<'db>,
|
||||||
) -> Result<Type<'db>, UnsupportedComparisonError<'db>> {
|
) -> Result<Type<'db>, UnsupportedComparisonError<'db>> {
|
||||||
// If either tuple is variable length, we can make no assumptions about the relative
|
match (left, right) {
|
||||||
// lengths of the tuples, and therefore neither about how they compare lexicographically.
|
// Both fixed-length: perform full lexicographic comparison.
|
||||||
// TODO: Consider comparing the prefixes of the tuples, since that could give a comparison
|
(TupleSpec::Fixed(left), TupleSpec::Fixed(right)) => {
|
||||||
// result regardless of how long the variable-length tuple is.
|
|
||||||
let (TupleSpec::Fixed(left), TupleSpec::Fixed(right)) = (left, right) else {
|
|
||||||
return Ok(Type::unknown());
|
|
||||||
};
|
|
||||||
|
|
||||||
let left_iter = left.elements().copied();
|
let left_iter = left.elements().copied();
|
||||||
let right_iter = right.elements().copied();
|
let right_iter = right.elements().copied();
|
||||||
|
|
||||||
|
|
@ -11207,7 +11202,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
for (l_ty, r_ty) in left_iter.zip(right_iter) {
|
for (l_ty, r_ty) in left_iter.zip(right_iter) {
|
||||||
let pairwise_eq_result = self
|
let pairwise_eq_result = self
|
||||||
.infer_binary_type_comparison(l_ty, ast::CmpOp::Eq, r_ty, range, visitor)
|
.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`");
|
.expect(
|
||||||
|
"infer_binary_type_comparison should never return None for `CmpOp::Eq`",
|
||||||
|
);
|
||||||
|
|
||||||
match pairwise_eq_result
|
match pairwise_eq_result
|
||||||
.try_bool(self.db())
|
.try_bool(self.db())
|
||||||
|
|
@ -11271,6 +11268,225 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
Ok(builder.build())
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn infer_subscript_expression(&mut self, subscript: &ast::ExprSubscript) -> Type<'db> {
|
fn infer_subscript_expression(&mut self, subscript: &ast::ExprSubscript) -> Type<'db> {
|
||||||
let ast::ExprSubscript {
|
let ast::ExprSubscript {
|
||||||
value,
|
value,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue