Same trick for tuples

This commit is contained in:
David Peter 2025-10-21 15:30:53 +02:00
parent be94796e6d
commit a3f8f60e1e
5 changed files with 32 additions and 18 deletions

View File

@ -2583,6 +2583,23 @@ class C:
reveal_type(C().x) # revealed: Unknown | tuple[Divergent, Literal[1]] reveal_type(C().x) # revealed: Unknown | tuple[Divergent, Literal[1]]
``` ```
That also works if the tuple is not constructed directly:
```py
# from typing import TypeVar, Literal
#
# T = TypeVar("T")
#
# def make_tuple(a: T) -> tuple[T, Literal[1]]:
# return (a, 1)
#
# class D:
# def f(self, other: "D"):
# self.x = make_tuple(other.x)
#
# reveal_type(D().x) # revealed: Unknown | tuple[Divergent, Literal[1]]
```
## Attributes of standard library modules that aren't yet defined ## Attributes of standard library modules that aren't yet defined
For attributes of stdlib modules that exist in future versions, we can give better diagnostics. For attributes of stdlib modules that exist in future versions, we can give better diagnostics.

View File

@ -301,7 +301,7 @@ fn expand_type<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option<Vec<Type<'db>>> {
} }
}) })
.multi_cartesian_product() .multi_cartesian_product()
.map(|types| Type::tuple(TupleType::heterogeneous(db, types))) .map(|types| Type::heterogeneous_tuple(db, types))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if expanded.len() == 1 { if expanded.len() == 1 {

View File

@ -5968,16 +5968,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let mut annotated_elt_tys = annotated_tuple.as_ref().map(Tuple::all_elements); let mut annotated_elt_tys = annotated_tuple.as_ref().map(Tuple::all_elements);
let db = self.db(); let db = self.db();
let divergent = Type::divergent(Some(self.scope()));
let element_types = elts.iter().map(|element| { let element_types = elts.iter().map(|element| {
let annotated_elt_ty = annotated_elt_tys.as_mut().and_then(Iterator::next).copied(); let annotated_elt_ty = annotated_elt_tys.as_mut().and_then(Iterator::next).copied();
let element_type = self.infer_expression(element, TypeContext::new(annotated_elt_ty)); self.infer_expression(element, TypeContext::new(annotated_elt_ty))
if element_type.has_divergent_type(self.db(), divergent) {
divergent
} else {
element_type
}
}); });
Type::heterogeneous_tuple(db, element_types) Type::heterogeneous_tuple(db, element_types)

View File

@ -73,13 +73,7 @@ impl<'db> Type<'db> {
{ {
Type::tuple(TupleType::heterogeneous( Type::tuple(TupleType::heterogeneous(
db, db,
elements.into_iter().map(Into::into).map(|ty| { elements.into_iter().map(Into::into),
if specialization_depth(db, ty) > MAX_SPECIALIZATION_DEPTH {
Type::divergent(None)
} else {
ty
}
}),
)) ))
} }

View File

@ -26,10 +26,11 @@ use crate::subscript::{Nth, OutOfBoundsError, PyIndex, PySlice, StepSizeZeroErro
use crate::types::class::{ClassType, KnownClass}; use crate::types::class::{ClassType, KnownClass};
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension}; use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
use crate::types::generics::InferableTypeVars; use crate::types::generics::InferableTypeVars;
use crate::types::visitor::MAX_SPECIALIZATION_DEPTH;
use crate::types::{ use crate::types::{
ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor, ApplyTypeMappingVisitor, BoundTypeVarInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, Type, TypeMapping, TypeRelation, IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, Type, TypeMapping, TypeRelation,
UnionBuilder, UnionType, UnionBuilder, UnionType, specialization_depth,
}; };
use crate::types::{Truthiness, TypeContext}; use crate::types::{Truthiness, TypeContext};
use crate::{Db, FxOrderSet, Program}; use crate::{Db, FxOrderSet, Program};
@ -178,7 +179,16 @@ impl<'db> TupleType<'db> {
db: &'db dyn Db, db: &'db dyn Db,
types: impl IntoIterator<Item = Type<'db>>, types: impl IntoIterator<Item = Type<'db>>,
) -> Option<Self> { ) -> Option<Self> {
TupleType::new(db, &TupleSpec::heterogeneous(types)) TupleType::new(
db,
&TupleSpec::heterogeneous(types.into_iter().map(|ty| {
if specialization_depth(db, ty) > MAX_SPECIALIZATION_DEPTH {
Type::divergent(None)
} else {
ty
}
})),
)
} }
#[cfg(test)] #[cfg(test)]