Skip to content

Commit b0e10a9

Browse files
[ty] Don't track inferability via different Type variants (#20677)
We have to track whether a typevar appears in a position where it's inferable or not. In a non-inferable position (in the body of the generic class or function that binds it), assignability must hold for every possible specialization of the typevar. In an inferable position, it only needs to hold for _some_ specialization. #20093 is working on using constraint sets to model assignability of typevars, and the constraint sets that we produce will be the same for inferable vs non-inferable typevars; what changes is what we _compare_ that constraint set to. (For a non-inferable typevar, the constraint set must equal the set of valid specializations; for an inferable typevar, it must not be `never`.) When I first added support for tracking inferable vs non-inferable typevars, it seemed like it would be easiest to have separate `Type` variants for each. The alternative (which lines up with the Δ set in [POPL15](https://doi.org/10.1145/2676726.2676991)) would be to explicitly plumb through a list of inferable typevars through our type property methods. That seemed cumbersome. In retrospect, that was the wrong decision. We've had to jump through hoops to translate types between the inferable and non-inferable variants, which has been quite brittle. Combined with the original point above, that much of the assignability logic will become more identical between inferable and non-inferable, there is less justification for the two `Type` variants. And plumbing an extra `inferable` parameter through all of these methods turns out to not be as bad as I anticipated. --------- Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
1 parent 25023cc commit b0e10a9

File tree

17 files changed

+312
-373
lines changed

17 files changed

+312
-373
lines changed

crates/ty_ide/src/completion.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ impl<'db> Completion<'db> {
131131
| Type::BytesLiteral(_) => CompletionKind::Value,
132132
Type::EnumLiteral(_) => CompletionKind::Enum,
133133
Type::ProtocolInstance(_) => CompletionKind::Interface,
134-
Type::NonInferableTypeVar(_) | Type::TypeVar(_) => CompletionKind::TypeParameter,
134+
Type::TypeVar(_) => CompletionKind::TypeParameter,
135135
Type::Union(union) => union
136136
.elements(db)
137137
.iter()

crates/ty_ide/src/semantic_tokens.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -336,9 +336,7 @@ impl<'db> SemanticTokenVisitor<'db> {
336336

337337
match ty {
338338
Type::ClassLiteral(_) => (SemanticTokenType::Class, modifiers),
339-
Type::NonInferableTypeVar(_) | Type::TypeVar(_) => {
340-
(SemanticTokenType::TypeParameter, modifiers)
341-
}
339+
Type::TypeVar(_) => (SemanticTokenType::TypeParameter, modifiers),
342340
Type::FunctionLiteral(_) => {
343341
// Check if this is a method based on current scope
344342
if self.in_class_scope {

crates/ty_python_semantic/src/types.rs

Lines changed: 77 additions & 240 deletions
Large diffs are not rendered by default.

crates/ty_python_semantic/src/types/bound_super.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ impl<'db> BoundSuperType<'db> {
343343
Type::TypeAlias(alias) => {
344344
return delegate_with_error_mapped(alias.value_type(db), None);
345345
}
346-
Type::TypeVar(type_var) | Type::NonInferableTypeVar(type_var) => {
346+
Type::TypeVar(type_var) => {
347347
let type_var = type_var.typevar(db);
348348
return match type_var.bound_or_constraints(db) {
349349
Some(TypeVarBoundOrConstraints::UpperBound(bound)) => {

crates/ty_python_semantic/src/types/builder.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,8 +1062,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
10621062
let mut positive_to_remove = SmallVec::<[usize; 1]>::new();
10631063

10641064
for (typevar_index, ty) in self.positive.iter().enumerate() {
1065-
let (Type::NonInferableTypeVar(bound_typevar) | Type::TypeVar(bound_typevar)) = ty
1066-
else {
1065+
let Type::TypeVar(bound_typevar) = ty else {
10671066
continue;
10681067
};
10691068
let Some(TypeVarBoundOrConstraints::Constraints(constraints)) =

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2589,7 +2589,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
25892589
return;
25902590
};
25912591

2592-
// TODO: Use the list of inferable typevars from the generic context of the callable.
2592+
self.inferable_typevars = generic_context.inferable_typevars(self.db);
25932593
let mut builder = SpecializationBuilder::new(self.db, self.inferable_typevars);
25942594

25952595
let parameters = self.signature.parameters();

crates/ty_python_semantic/src/types/class.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,15 +1627,10 @@ impl<'db> ClassLiteral<'db> {
16271627
})
16281628
}
16291629

1630-
/// Returns a specialization of this class where each typevar is mapped to itself. The second
1631-
/// parameter can be `Type::TypeVar` or `Type::NonInferableTypeVar`, depending on the use case.
1632-
pub(crate) fn identity_specialization(
1633-
self,
1634-
db: &'db dyn Db,
1635-
typevar_to_type: &impl Fn(BoundTypeVarInstance<'db>) -> Type<'db>,
1636-
) -> ClassType<'db> {
1630+
/// Returns a specialization of this class where each typevar is mapped to itself.
1631+
pub(crate) fn identity_specialization(self, db: &'db dyn Db) -> ClassType<'db> {
16371632
self.apply_specialization(db, |generic_context| {
1638-
generic_context.identity_specialization(db, typevar_to_type)
1633+
generic_context.identity_specialization(db)
16391634
})
16401635
}
16411636

crates/ty_python_semantic/src/types/class_base.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ impl<'db> ClassBase<'db> {
155155
| Type::StringLiteral(_)
156156
| Type::LiteralString
157157
| Type::ModuleLiteral(_)
158-
| Type::NonInferableTypeVar(_)
159158
| Type::TypeVar(_)
160159
| Type::BoundSuper(_)
161160
| Type::ProtocolInstance(_)

crates/ty_python_semantic/src/types/display.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -560,9 +560,7 @@ impl Display for DisplayRepresentation<'_> {
560560
.display_with(self.db, self.settings.clone()),
561561
literal_name = enum_literal.name(self.db)
562562
),
563-
Type::NonInferableTypeVar(bound_typevar) | Type::TypeVar(bound_typevar) => {
564-
bound_typevar.identity(self.db).display(self.db).fmt(f)
565-
}
563+
Type::TypeVar(bound_typevar) => bound_typevar.identity(self.db).display(self.db).fmt(f),
566564
Type::AlwaysTruthy => f.write_str("AlwaysTruthy"),
567565
Type::AlwaysFalsy => f.write_str("AlwaysFalsy"),
568566
Type::BoundSuper(bound_super) => {

crates/ty_python_semantic/src/types/function.rs

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -365,28 +365,12 @@ impl<'db> OverloadLiteral<'db> {
365365
if function_node.is_async && !is_generator {
366366
signature = signature.wrap_coroutine_return_type(db);
367367
}
368-
signature = signature.mark_typevars_inferable(db);
369-
370-
let pep695_ctx = function_node.type_params.as_ref().map(|type_params| {
371-
GenericContext::from_type_params(db, index, self.definition(db), type_params)
372-
});
373-
let legacy_ctx = GenericContext::from_function_params(
374-
db,
375-
self.definition(db),
376-
signature.parameters(),
377-
signature.return_ty,
378-
);
379-
// We need to update `signature.generic_context` here,
380-
// because type variables in `GenericContext::variables` are still non-inferable.
381-
signature.generic_context =
382-
GenericContext::merge_pep695_and_legacy(db, pep695_ctx, legacy_ctx);
383368

384369
signature
385370
}
386371

387372
/// Typed internally-visible "raw" signature for this function.
388-
/// That is, type variables in parameter types and the return type remain non-inferable,
389-
/// and the return types of async functions are not wrapped in `CoroutineType[...]`.
373+
/// That is, the return types of async functions are not wrapped in `CoroutineType[...]`.
390374
///
391375
/// ## Warning
392376
///
@@ -1133,7 +1117,6 @@ fn is_instance_truthiness<'db>(
11331117
| Type::PropertyInstance(..)
11341118
| Type::AlwaysTruthy
11351119
| Type::AlwaysFalsy
1136-
| Type::NonInferableTypeVar(..)
11371120
| Type::TypeVar(..)
11381121
| Type::BoundSuper(..)
11391122
| Type::TypeIs(..)
@@ -1729,11 +1712,7 @@ impl KnownFunction {
17291712
}
17301713

17311714
KnownFunction::RangeConstraint => {
1732-
let [
1733-
Some(lower),
1734-
Some(Type::NonInferableTypeVar(typevar)),
1735-
Some(upper),
1736-
] = parameter_types
1715+
let [Some(lower), Some(Type::TypeVar(typevar)), Some(upper)] = parameter_types
17371716
else {
17381717
return;
17391718
};
@@ -1746,11 +1725,7 @@ impl KnownFunction {
17461725
}
17471726

17481727
KnownFunction::NegatedRangeConstraint => {
1749-
let [
1750-
Some(lower),
1751-
Some(Type::NonInferableTypeVar(typevar)),
1752-
Some(upper),
1753-
] = parameter_types
1728+
let [Some(lower), Some(Type::TypeVar(typevar)), Some(upper)] = parameter_types
17541729
else {
17551730
return;
17561731
};

0 commit comments

Comments
 (0)