Skip to content

Commit c8942cb

Browse files
committed
wip
1 parent c6b4a9f commit c8942cb

File tree

5 files changed

+65
-47
lines changed

5 files changed

+65
-47
lines changed

crates/ty_python_semantic/resources/mdtest/subscript/tuple.md

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,13 @@ class HeterogeneousSubclass0(tuple[()]): ...
3939
reveal_type(HeterogeneousSubclass0.__getitem__)
4040

4141
def f0(h0: HeterogeneousSubclass0, i: int):
42-
reveal_type(h0[0]) # revealed: Never
43-
reveal_type(h0[1]) # revealed: Never
44-
reveal_type(h0[-1]) # revealed: Never
42+
# error: [index-out-of-bounds]
43+
reveal_type(h0[0]) # revealed: Unknown
44+
# error: [index-out-of-bounds]
45+
reveal_type(h0[1]) # revealed: Unknown
46+
# error: [index-out-of-bounds]
47+
reveal_type(h0[-1]) # revealed: Unknown
48+
4549
reveal_type(h0[i]) # revealed: Never
4650

4751
class HeterogeneousSubclass1(tuple[I0]): ...
@@ -51,7 +55,8 @@ reveal_type(HeterogeneousSubclass1.__getitem__)
5155

5256
def f0(h1: HeterogeneousSubclass1, i: int):
5357
reveal_type(h1[0]) # revealed: I0
54-
reveal_type(h1[1]) # revealed: I0
58+
# error: [index-out-of-bounds]
59+
reveal_type(h1[1]) # revealed: Unknown
5560
reveal_type(h1[-1]) # revealed: I0
5661
reveal_type(h1[i]) # revealed: I0
5762

@@ -84,24 +89,19 @@ def g(m: MixedSubclass, i: int):
8489
reveal_type(m[2]) # revealed: I1 | I2 | I3
8590
reveal_type(m[3]) # revealed: I1 | I2 | I3
8691
reveal_type(m[4]) # revealed: I1 | I2 | I3 | I5
92+
reveal_type(m[5]) # revealed: I1 | I2 | I3 | I5
93+
reveal_type(m[10]) # revealed: I1 | I2 | I3 | I5
8794

8895
reveal_type(m[-1]) # revealed: I5
8996
reveal_type(m[-2]) # revealed: I2
9097
reveal_type(m[-3]) # revealed: I3
9198
reveal_type(m[-4]) # revealed: I2
92-
reveal_type(m[-5]) # revealed: I1 | I0
99+
reveal_type(m[-5]) # revealed: I0 | I1
100+
reveal_type(m[-6]) # revealed: I0 | I1
101+
reveal_type(m[-10]) # revealed: I0 | I1
93102

94103
reveal_type(m[i]) # revealed: I0 | I1 | I2 | I3 | I5
95104

96-
# Ideally we would not include `I0` in the unions for these,
97-
# but it's not possible to do this using only synthesized overloads.
98-
reveal_type(m[5]) # revealed: I0 | I1 | I2 | I3 | I5
99-
reveal_type(m[10]) # revealed: I0 | I1 | I2 | I3 | I5
100-
101-
# Similarly, ideally these would just be `I0` | I1`,
102-
# but achieving that with only synthesized overloads wouldn't be possible
103-
reveal_type(m[-6]) # revealed: I0 | I1 | I2 | I3 | I5
104-
reveal_type(m[-10]) # revealed: I0 | I1 | I2 | I3 | I5
105105

106106
class MixedSubclass2(tuple[I0, I1, *tuple[I2, ...], I3]): ...
107107

@@ -112,18 +112,12 @@ def g(m: MixedSubclass2, i: int):
112112
reveal_type(m[0]) # revealed: I0
113113
reveal_type(m[1]) # revealed: I1
114114
reveal_type(m[2]) # revealed: I2 | I3
115-
116-
# Ideally this would just be `I2 | I3`,
117-
# but that's not possible to achieve with synthesized overloads
118-
reveal_type(m[3]) # revealed: I0 | I1 | I2 | I3
115+
reveal_type(m[3]) # revealed: I2 | I3
119116

120117
reveal_type(m[-1]) # revealed: I3
121-
reveal_type(m[-2]) # revealed: I2 | I1
122-
reveal_type(m[-3]) # revealed: I2 | I1 | I0
123-
124-
# Ideally this would just be `I2 | I1 | I0`,
125-
# but that's not possible to achieve with synthesized overloads
126-
reveal_type(m[-4]) # revealed: I0 | I1 | I2 | I3
118+
reveal_type(m[-2]) # revealed: I1 | I2
119+
reveal_type(m[-3]) # revealed: I0 | I1 | I2
120+
reveal_type(m[-4]) # revealed: I0 | I1 | I2
127121
```
128122

129123
The stdlib API `os.stat` is a commonly used API that returns an instance of a tuple subclass

crates/ty_python_semantic/src/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2160,7 +2160,7 @@ impl<'db> Type<'db> {
21602160
}
21612161

21622162
(Type::NominalInstance(left), Type::NominalInstance(right)) => {
2163-
left.is_disjoint_from_impl(db, right)
2163+
left.is_disjoint_from_impl(db, right, visitor)
21642164
}
21652165

21662166
(Type::PropertyInstance(_), other) | (other, Type::PropertyInstance(_)) => {

crates/ty_python_semantic/src/types/infer.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8270,7 +8270,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
82708270
if class.is_tuple(self.db()) {
82718271
return self
82728272
.infer_tuple_type_expression(slice)
8273-
.to_meta_type(self.db());
8273+
.into_nominal_instance()
8274+
.map(|instance| Type::from(instance.class))
8275+
.unwrap_or_else(Type::unknown);
82748276
}
82758277
if let Some(generic_context) = class.generic_context(self.db()) {
82768278
return self.infer_explicit_class_specialization(
@@ -8284,7 +8286,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
82848286
if let Type::SpecialForm(SpecialFormType::Tuple) = value_ty {
82858287
return self
82868288
.infer_tuple_type_expression(slice)
8287-
.to_meta_type(self.db());
8289+
.into_nominal_instance()
8290+
.map(|instance| Type::from(instance.class))
8291+
.unwrap_or_else(Type::unknown);
82888292
}
82898293

82908294
let slice_ty = self.infer_expression(slice);

crates/ty_python_semantic/src/types/instance.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,9 @@ use super::{ClassType, KnownClass, SubclassOfType, Type, TypeVarVariance};
77
use crate::place::PlaceAndQualifiers;
88
use crate::types::cyclic::PairVisitor;
99
use crate::types::enums::is_single_member_enum;
10-
use crate::types::generics::{GenericContext, Specialization};
1110
use crate::types::protocol_class::walk_protocol_interface;
1211
use crate::types::tuple::TupleType;
13-
use crate::types::{
14-
DynamicType, GenericAlias, TypeMapping, TypeRelation, TypeTransformer, TypeVarInstance,
15-
UnionType,
16-
};
12+
use crate::types::{DynamicType, TypeMapping, TypeRelation, TypeTransformer, TypeVarInstance};
1713
use crate::{Db, FxOrderSet};
1814

1915
pub(super) use synthesized_protocol::SynthesizedProtocolType;
@@ -128,7 +124,21 @@ impl<'db> NominalInstanceType<'db> {
128124
self.class.is_equivalent_to(db, other.class)
129125
}
130126

131-
pub(super) fn is_disjoint_from_impl(self, db: &'db dyn Db, other: Self) -> bool {
127+
pub(super) fn is_disjoint_from_impl(
128+
self,
129+
db: &'db dyn Db,
130+
other: Self,
131+
visitor: &mut PairVisitor<'db>,
132+
) -> bool {
133+
let self_spec = self.class.tuple_spec(db);
134+
if let Some(self_spec) = self_spec.as_deref() {
135+
let other_spec = other.class.tuple_spec(db);
136+
if let Some(other_spec) = other_spec.as_deref() {
137+
if self_spec.is_disjoint_from_impl(db, other_spec, visitor) {
138+
return true;
139+
}
140+
}
141+
}
132142
!self.class.could_coexist_in_mro_with(db, other.class)
133143
}
134144

crates/ty_python_semantic/src/types/tuple.rs

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,10 +1116,10 @@ impl<'db> Tuple<Type<'db>> {
11161116
}
11171117
}
11181118

1119-
fn is_disjoint_from_impl(
1120-
&'db self,
1119+
pub(super) fn is_disjoint_from_impl(
1120+
&self,
11211121
db: &'db dyn Db,
1122-
other: &'db Self,
1122+
other: &Self,
11231123
visitor: &mut PairVisitor<'db>,
11241124
) -> bool {
11251125
// Two tuples with an incompatible number of required elements must always be disjoint.
@@ -1136,35 +1136,40 @@ impl<'db> Tuple<Type<'db>> {
11361136
#[allow(clippy::items_after_statements)]
11371137
fn any_disjoint<'db>(
11381138
db: &'db dyn Db,
1139-
a: impl IntoIterator<Item = &'db Type<'db>>,
1140-
b: impl IntoIterator<Item = &'db Type<'db>>,
1139+
a: impl IntoIterator<Item = Type<'db>>,
1140+
b: impl IntoIterator<Item = Type<'db>>,
11411141
visitor: &mut PairVisitor<'db>,
11421142
) -> bool {
11431143
a.into_iter().zip(b).any(|(self_element, other_element)| {
1144-
self_element.is_disjoint_from_impl(db, *other_element, visitor)
1144+
self_element.is_disjoint_from_impl(db, other_element, visitor)
11451145
})
11461146
}
11471147

11481148
match (self, other) {
11491149
(Tuple::Fixed(self_tuple), Tuple::Fixed(other_tuple)) => {
1150-
if any_disjoint(db, self_tuple.elements(), other_tuple.elements(), visitor) {
1150+
if any_disjoint(
1151+
db,
1152+
self_tuple.elements().copied(),
1153+
other_tuple.elements().copied(),
1154+
visitor,
1155+
) {
11511156
return true;
11521157
}
11531158
}
11541159

11551160
(Tuple::Variable(self_tuple), Tuple::Variable(other_tuple)) => {
11561161
if any_disjoint(
11571162
db,
1158-
self_tuple.prefix_elements(),
1159-
other_tuple.prefix_elements(),
1163+
self_tuple.prefix_elements().copied(),
1164+
other_tuple.prefix_elements().copied(),
11601165
visitor,
11611166
) {
11621167
return true;
11631168
}
11641169
if any_disjoint(
11651170
db,
1166-
self_tuple.suffix_elements().rev(),
1167-
other_tuple.suffix_elements().rev(),
1171+
self_tuple.suffix_elements().rev().copied(),
1172+
other_tuple.suffix_elements().rev().copied(),
11681173
visitor,
11691174
) {
11701175
return true;
@@ -1173,13 +1178,18 @@ impl<'db> Tuple<Type<'db>> {
11731178

11741179
(Tuple::Fixed(fixed), Tuple::Variable(variable))
11751180
| (Tuple::Variable(variable), Tuple::Fixed(fixed)) => {
1176-
if any_disjoint(db, fixed.elements(), variable.prefix_elements(), visitor) {
1181+
if any_disjoint(
1182+
db,
1183+
fixed.elements().copied(),
1184+
variable.prefix_elements().copied(),
1185+
visitor,
1186+
) {
11771187
return true;
11781188
}
11791189
if any_disjoint(
11801190
db,
1181-
fixed.elements().rev(),
1182-
variable.suffix_elements().rev(),
1191+
fixed.elements().rev().copied(),
1192+
variable.suffix_elements().rev().copied(),
11831193
visitor,
11841194
) {
11851195
return true;

0 commit comments

Comments
 (0)