Skip to content

Commit 587dae6

Browse files
committed
make inferable optional
1 parent 2ea63ed commit 587dae6

4 files changed

Lines changed: 61 additions & 47 deletions

File tree

crates/ty_python_semantic/resources/mdtest/type_properties/satisfied_by_all_typevars.md

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -61,30 +61,30 @@ class Unrelated: ...
6161

6262
def unbounded[T]():
6363
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T]))
64-
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[()]))
64+
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
6565

6666
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T]))
67-
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[()]))
67+
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
6868

6969
# (T = Never) is a valid specialization, which satisfies (T ≤ Unrelated).
7070
static_assert(ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[T]))
7171
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated).
72-
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[()]))
72+
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
7373

7474
# (T = Base) is a valid specialization, which satisfies (T ≤ Super).
7575
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars(inferable=tuple[T]))
7676
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Super).
77-
static_assert(not ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars(inferable=tuple[()]))
77+
static_assert(not ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
7878

7979
# (T = Base) is a valid specialization, which satisfies (T ≤ Base).
8080
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T]))
8181
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Base).
82-
static_assert(not ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[()]))
82+
static_assert(not ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
8383

8484
# (T = Sub) is a valid specialization, which satisfies (T ≤ Sub).
8585
static_assert(ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars(inferable=tuple[T]))
8686
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Sub).
87-
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars(inferable=tuple[()]))
87+
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars())
8888
```
8989

9090
## Typevar with an upper bound
@@ -107,38 +107,38 @@ class Unrelated: ...
107107

108108
def bounded[T: Base]():
109109
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T]))
110-
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[()]))
110+
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
111111

112112
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T]))
113-
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[()]))
113+
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
114114

115115
# (T = Base) is a valid specialization, which satisfies (T ≤ Super).
116116
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars(inferable=tuple[T]))
117117
# Every valid specialization satisfies (T ≤ Base). Since (Base ≤ Super), every valid
118118
# specialization also satisfies (T ≤ Super).
119-
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars(inferable=tuple[()]))
119+
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
120120

121121
# (T = Base) is a valid specialization, which satisfies (T ≤ Base).
122122
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T]))
123123
# Every valid specialization satisfies (T ≤ Base).
124-
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[()]))
124+
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
125125

126126
# (T = Sub) is a valid specialization, which satisfies (T ≤ Sub).
127127
static_assert(ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars(inferable=tuple[T]))
128128
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Sub).
129-
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars(inferable=tuple[()]))
129+
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars())
130130

131131
# (T = Never) is a valid specialization, which satisfies (T ≤ Unrelated).
132132
constraints = ConstraintSet.range(Never, T, Unrelated)
133133
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
134134
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated).
135-
static_assert(not constraints.satisfied_by_all_typevars(inferable=tuple[()]))
135+
static_assert(not constraints.satisfied_by_all_typevars())
136136

137137
# Never is the only type that satisfies both (T ≤ Base) and (T ≤ Unrelated). So there is no
138138
# valid specialization that satisfies (T ≤ Unrelated ∧ T ≠ Never).
139139
constraints = constraints & ~ConstraintSet.range(Never, T, Never)
140140
static_assert(not constraints.satisfied_by_all_typevars(inferable=tuple[T]))
141-
static_assert(not constraints.satisfied_by_all_typevars(inferable=tuple[()]))
141+
static_assert(not constraints.satisfied_by_all_typevars())
142142
```
143143

144144
## Constrained typevar
@@ -161,60 +161,60 @@ class Unrelated: ...
161161

162162
def constrained[T: (Base, Unrelated)]():
163163
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[T]))
164-
static_assert(ConstraintSet.always().satisfied_by_all_typevars(inferable=tuple[()]))
164+
static_assert(ConstraintSet.always().satisfied_by_all_typevars())
165165

166166
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[T]))
167-
static_assert(not ConstraintSet.never().satisfied_by_all_typevars(inferable=tuple[()]))
167+
static_assert(not ConstraintSet.never().satisfied_by_all_typevars())
168168

169169
# (T = Unrelated) is a valid specialization, which satisfies (T ≤ Unrelated).
170170
static_assert(ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[T]))
171171
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Unrelated).
172-
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars(inferable=tuple[()]))
172+
static_assert(not ConstraintSet.range(Never, T, Unrelated).satisfied_by_all_typevars())
173173

174174
# (T = Base) is a valid specialization, which satisfies (T ≤ Super).
175175
static_assert(ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars(inferable=tuple[T]))
176176
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Super).
177-
static_assert(not ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars(inferable=tuple[()]))
177+
static_assert(not ConstraintSet.range(Never, T, Super).satisfied_by_all_typevars())
178178

179179
# (T = Base) is a valid specialization, which satisfies (T ≤ Base).
180180
static_assert(ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[T]))
181181
# (T = Unrelated) is a valid specialization, which does not satisfy (T ≤ Base).
182-
static_assert(not ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars(inferable=tuple[()]))
182+
static_assert(not ConstraintSet.range(Never, T, Base).satisfied_by_all_typevars())
183183

184184
# Neither (T = Base) nor (T = Unrelated) satisfy (T ≤ Sub).
185185
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars(inferable=tuple[T]))
186-
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars(inferable=tuple[()]))
186+
static_assert(not ConstraintSet.range(Never, T, Sub).satisfied_by_all_typevars())
187187

188188
# (T = Base) and (T = Unrelated) both satisfy (T ≤ Super ∨ T ≤ Unrelated).
189189
constraints = ConstraintSet.range(Never, T, Super) | ConstraintSet.range(Never, T, Unrelated)
190190
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
191-
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[()]))
191+
static_assert(constraints.satisfied_by_all_typevars())
192192

193193
# (T = Base) and (T = Unrelated) both satisfy (T ≤ Base ∨ T ≤ Unrelated).
194194
constraints = ConstraintSet.range(Never, T, Base) | ConstraintSet.range(Never, T, Unrelated)
195195
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
196-
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[()]))
196+
static_assert(constraints.satisfied_by_all_typevars())
197197

198198
# (T = Unrelated) is a valid specialization, which satisfies (T ≤ Sub ∨ T ≤ Unrelated).
199199
constraints = ConstraintSet.range(Never, T, Sub) | ConstraintSet.range(Never, T, Unrelated)
200200
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
201201
# (T = Base) is a valid specialization, which does not satisfy (T ≤ Sub ∨ T ≤ Unrelated).
202-
static_assert(not constraints.satisfied_by_all_typevars(inferable=tuple[()]))
202+
static_assert(not constraints.satisfied_by_all_typevars())
203203

204204
# (T = Unrelated) is a valid specialization, which satisfies (T = Super ∨ T = Unrelated).
205205
constraints = ConstraintSet.range(Super, T, Super) | ConstraintSet.range(Unrelated, T, Unrelated)
206206
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
207207
# (T = Base) is a valid specialization, which does not satisfy (T = Super ∨ T = Unrelated).
208-
static_assert(not constraints.satisfied_by_all_typevars(inferable=tuple[()]))
208+
static_assert(not constraints.satisfied_by_all_typevars())
209209

210210
# (T = Base) and (T = Unrelated) both satisfy (T = Base ∨ T = Unrelated).
211211
constraints = ConstraintSet.range(Base, T, Base) | ConstraintSet.range(Unrelated, T, Unrelated)
212212
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
213-
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[()]))
213+
static_assert(constraints.satisfied_by_all_typevars())
214214

215215
# (T = Unrelated) is a valid specialization, which satisfies (T = Sub ∨ T = Unrelated).
216216
constraints = ConstraintSet.range(Sub, T, Sub) | ConstraintSet.range(Unrelated, T, Unrelated)
217217
static_assert(constraints.satisfied_by_all_typevars(inferable=tuple[T]))
218218
# (T = Base) is a valid specialization, which does not satisfy (T = Sub ∨ T = Unrelated).
219-
static_assert(not constraints.satisfied_by_all_typevars(inferable=tuple[()]))
219+
static_assert(not constraints.satisfied_by_all_typevars())
220220
```

crates/ty_python_semantic/src/types.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10743,7 +10743,11 @@ impl<'db> KnownBoundMethodType<'db> {
1074310743
Either::Right(std::iter::once(Signature::new(
1074410744
Parameters::new([Parameter::keyword_only(Name::new_static("inferable"))
1074510745
.type_form()
10746-
.with_annotated_type(Type::homogeneous_tuple(db, Type::any()))]),
10746+
.with_annotated_type(UnionType::from_elements(
10747+
db,
10748+
[Type::homogeneous_tuple(db, Type::any()), Type::none(db)],
10749+
))
10750+
.with_default_type(Type::none(db))]),
1074710751
Some(KnownClass::Bool.to_instance(db)),
1074810752
)))
1074910753
}

crates/ty_python_semantic/src/types/call/bind.rs

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ use crate::types::signatures::{Parameter, ParameterForm, ParameterKind, Paramete
3636
use crate::types::tuple::{TupleLength, TupleType};
3737
use crate::types::{
3838
BoundMethodType, ClassLiteral, DataclassFlags, DataclassParams, FieldInstance,
39-
KnownBoundMethodType, KnownClass, KnownInstanceType, MemberLookupPolicy, PropertyInstanceType,
40-
SpecialFormType, TrackedConstraintSet, TypeAliasType, TypeContext, UnionBuilder, UnionType,
41-
WrapperDescriptorKind, enums, ide_support, infer_isolated_expression, todo_type,
39+
KnownBoundMethodType, KnownClass, KnownInstanceType, MemberLookupPolicy, NominalInstanceType,
40+
PropertyInstanceType, SpecialFormType, TrackedConstraintSet, TypeAliasType, TypeContext,
41+
UnionBuilder, UnionType, WrapperDescriptorKind, enums, ide_support, infer_isolated_expression,
42+
todo_type,
4243
};
4344
use ruff_db::diagnostic::{Annotation, Diagnostic, SubDiagnostic, SubDiagnosticSeverity};
4445
use ruff_python_ast::{self as ast, ArgOrKeyword, PythonVersion};
@@ -1178,24 +1179,31 @@ impl<'db> Bindings<'db> {
11781179
Type::KnownBoundMethod(
11791180
KnownBoundMethodType::ConstraintSetSatisfiedByAllTypeVars(tracked),
11801181
) => {
1181-
let [Some(inferable)] = overload.parameter_types() else {
1182-
continue;
1183-
};
1184-
let Some(tuple) = inferable
1185-
.as_nominal_instance()
1186-
.and_then(|instance| instance.tuple_spec(db))
1187-
else {
1188-
continue;
1182+
let extract_inferable = |instance: &NominalInstanceType<'db>| {
1183+
if instance.has_known_class(db, KnownClass::NoneType) {
1184+
// Caller explicitly passed None, so no typevars are inferable.
1185+
return Some(FxHashSet::default());
1186+
}
1187+
instance
1188+
.tuple_spec(db)?
1189+
.fixed_elements()
1190+
.map(|ty| {
1191+
ty.as_typevar()
1192+
.map(|bound_typevar| bound_typevar.identity(db))
1193+
})
1194+
.collect()
11891195
};
1190-
let inferable: Option<FxHashSet<_>> = tuple
1191-
.fixed_elements()
1192-
.map(|ty| {
1193-
ty.as_typevar()
1194-
.map(|bound_typevar| bound_typevar.identity(db))
1195-
})
1196-
.collect();
1197-
let Some(inferable) = inferable else {
1198-
continue;
1196+
1197+
let inferable = match overload.parameter_types() {
1198+
// Caller did not provide argument, so no typevars are inferable.
1199+
[None] => FxHashSet::default(),
1200+
[Some(Type::NominalInstance(instance))] => {
1201+
match extract_inferable(instance) {
1202+
Some(inferable) => inferable,
1203+
None => continue,
1204+
}
1205+
}
1206+
_ => continue,
11991207
};
12001208

12011209
let result = tracked

crates/ty_vendored/ty_extensions/ty_extensions.pyi

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ class ConstraintSet:
6767
.. _subtype: https://typing.python.org/en/latest/spec/concepts.html#subtype-supertype-and-type-equivalence
6868
"""
6969

70-
def satisfied_by_all_typevars(self, *, inferable: tuple[Any, ...]) -> bool:
70+
def satisfied_by_all_typevars(
71+
self, *, inferable: tuple[Any, ...] | None = None
72+
) -> bool:
7173
"""
7274
Returns whether this constraint set is satisfied by all of the typevars
7375
that it mentions. You must provide a tuple of the typevars that should

0 commit comments

Comments
 (0)