mirror of https://github.com/astral-sh/ruff
Same trick for tuples
This commit is contained in:
parent
d2f73a404a
commit
be94796e6d
|
|
@ -2576,13 +2576,11 @@ reveal_type(Answer.__members__) # revealed: MappingProxyType[str, Unknown]
|
|||
## Divergent inferred implicit instance attribute types
|
||||
|
||||
```py
|
||||
# TODO: This test currently panics, see https://github.com/astral-sh/ty/issues/837
|
||||
class C:
|
||||
def f(self, other: "C"):
|
||||
self.x = (other.x, 1)
|
||||
|
||||
# class C:
|
||||
# def f(self, other: "C"):
|
||||
# self.x = (other.x, 1)
|
||||
#
|
||||
# reveal_type(C().x) # revealed: Unknown | tuple[Divergent, Literal[1]]
|
||||
reveal_type(C().x) # revealed: Unknown | tuple[Divergent, Literal[1]]
|
||||
```
|
||||
|
||||
## Attributes of standard library modules that aren't yet defined
|
||||
|
|
|
|||
|
|
@ -827,7 +827,7 @@ impl<'db> Type<'db> {
|
|||
Self::Dynamic(DynamicType::Unknown)
|
||||
}
|
||||
|
||||
pub(crate) fn divergent(scope: ScopeId<'db>) -> Self {
|
||||
pub(crate) fn divergent(scope: Option<ScopeId<'db>>) -> Self {
|
||||
Self::Dynamic(DynamicType::Divergent(DivergentType { scope }))
|
||||
}
|
||||
|
||||
|
|
@ -7646,7 +7646,7 @@ impl<'db> KnownInstanceType<'db> {
|
|||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, salsa::Update, get_size2::GetSize)]
|
||||
pub struct DivergentType<'db> {
|
||||
/// The scope where this divergence was detected.
|
||||
scope: ScopeId<'db>,
|
||||
scope: Option<ScopeId<'db>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, salsa::Update, get_size2::GetSize)]
|
||||
|
|
@ -11751,7 +11751,7 @@ pub(crate) mod tests {
|
|||
let file_scope_id = FileScopeId::global();
|
||||
let scope = file_scope_id.to_scope_id(&db, file);
|
||||
|
||||
let div = Type::Dynamic(DynamicType::Divergent(DivergentType { scope }));
|
||||
let div = Type::Dynamic(DynamicType::Divergent(DivergentType { scope: Some(scope) }));
|
||||
|
||||
// The `Divergent` type must not be eliminated in union with other dynamic types,
|
||||
// as this would prevent detection of divergent type inference using `Divergent`.
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@ use crate::types::member::{Member, class_member};
|
|||
use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signature};
|
||||
use crate::types::tuple::{TupleSpec, TupleType};
|
||||
use crate::types::typed_dict::typed_dict_params_from_class_def;
|
||||
use crate::types::visitor::{NonAtomicType, TypeKind, TypeVisitor, walk_non_atomic_type};
|
||||
use crate::types::visitor::{
|
||||
MAX_SPECIALIZATION_DEPTH, NonAtomicType, TypeKind, TypeVisitor, walk_non_atomic_type,
|
||||
};
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, Binding, BoundSuperType, CallableType, DataclassFlags,
|
||||
DataclassParams, DeprecatedInstance, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
||||
|
|
@ -1609,19 +1611,6 @@ impl<'db> ClassLiteral<'db> {
|
|||
db: &'db dyn Db,
|
||||
f: impl FnOnce(GenericContext<'db>) -> Specialization<'db>,
|
||||
) -> ClassType<'db> {
|
||||
// To prevent infinite recursion during type inference for infinite types, we fall back to
|
||||
// `C[Divergent]` once a certain amount of levels of specialization have occurred. For
|
||||
// example:
|
||||
//
|
||||
// ```py
|
||||
// x = 1
|
||||
// while random_bool():
|
||||
// x = [x]
|
||||
//
|
||||
// reveal_type(x) # Unknown | Literal[1] | list[Divergent]
|
||||
// ```
|
||||
const MAX_SPECIALIZATION_DEPTH: usize = 10;
|
||||
|
||||
match self.generic_context(db) {
|
||||
None => ClassType::NonGeneric(self),
|
||||
Some(generic_context) => {
|
||||
|
|
@ -1629,11 +1618,8 @@ impl<'db> ClassLiteral<'db> {
|
|||
|
||||
for (idx, ty) in specialization.types(db).iter().enumerate() {
|
||||
if specialization_depth(db, *ty) > MAX_SPECIALIZATION_DEPTH {
|
||||
specialization = specialization.with_replaced_type(
|
||||
db,
|
||||
idx,
|
||||
Type::divergent(self.body_scope(db)),
|
||||
);
|
||||
specialization =
|
||||
specialization.with_replaced_type(db, idx, Type::divergent(None));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ mod builder;
|
|||
mod tests;
|
||||
|
||||
/// How many fixpoint iterations to allow before falling back to Divergent type.
|
||||
const ITERATIONS_BEFORE_FALLBACK: u32 = 10;
|
||||
const ITERATIONS_BEFORE_FALLBACK: u32 = 20;
|
||||
|
||||
/// Infer all types for a [`ScopeId`], including all definitions and expressions in that scope.
|
||||
/// Use when checking a scope, or needing to provide a type for an arbitrary expression in the
|
||||
|
|
@ -567,7 +567,7 @@ impl<'db> CycleRecovery<'db> {
|
|||
fn fallback_type(self) -> Type<'db> {
|
||||
match self {
|
||||
Self::Initial => Type::Never,
|
||||
Self::Divergent(scope) => Type::divergent(scope),
|
||||
Self::Divergent(scope) => Type::divergent(Some(scope)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5968,7 +5968,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
let mut annotated_elt_tys = annotated_tuple.as_ref().map(Tuple::all_elements);
|
||||
|
||||
let db = self.db();
|
||||
let divergent = Type::divergent(self.scope());
|
||||
let divergent = Type::divergent(Some(self.scope()));
|
||||
let element_types = elts.iter().map(|element| {
|
||||
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));
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
/// Infer the type of a type expression.
|
||||
pub(super) fn infer_type_expression(&mut self, expression: &ast::Expr) -> Type<'db> {
|
||||
let mut ty = self.infer_type_expression_no_store(expression);
|
||||
let divergent = Type::divergent(self.scope());
|
||||
let divergent = Type::divergent(Some(self.scope()));
|
||||
if ty.has_divergent_type(self.db(), divergent) {
|
||||
ty = divergent;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,11 @@ use crate::types::enums::is_single_member_enum;
|
|||
use crate::types::generics::{InferableTypeVars, walk_specialization};
|
||||
use crate::types::protocol_class::walk_protocol_interface;
|
||||
use crate::types::tuple::{TupleSpec, TupleType};
|
||||
use crate::types::visitor::MAX_SPECIALIZATION_DEPTH;
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, ClassBase, ClassLiteral, FindLegacyTypeVarsVisitor,
|
||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, NormalizedVisitor, TypeContext,
|
||||
TypeMapping, TypeRelation, VarianceInferable,
|
||||
TypeMapping, TypeRelation, VarianceInferable, specialization_depth,
|
||||
};
|
||||
use crate::{Db, FxOrderSet};
|
||||
|
||||
|
|
@ -72,7 +73,13 @@ impl<'db> Type<'db> {
|
|||
{
|
||||
Type::tuple(TupleType::heterogeneous(
|
||||
db,
|
||||
elements.into_iter().map(Into::into),
|
||||
elements.into_iter().map(Into::into).map(|ty| {
|
||||
if specialization_depth(db, ty) > MAX_SPECIALIZATION_DEPTH {
|
||||
Type::divergent(None)
|
||||
} else {
|
||||
ty
|
||||
}
|
||||
}),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -301,6 +301,19 @@ pub(super) fn any_over_type<'db>(
|
|||
visitor.found_matching_type.get()
|
||||
}
|
||||
|
||||
// To prevent infinite recursion during type inference for infinite types, we fall back to
|
||||
// `C[Divergent]` once a certain amount of levels of specialization have occurred. For
|
||||
// example:
|
||||
//
|
||||
// ```py
|
||||
// x = 1
|
||||
// while random_bool():
|
||||
// x = [x]
|
||||
//
|
||||
// reveal_type(x) # Unknown | Literal[1] | list[Divergent]
|
||||
// ```
|
||||
pub(super) const MAX_SPECIALIZATION_DEPTH: usize = 10;
|
||||
|
||||
/// Returns the maximum number of layers of generic specializations for a given type.
|
||||
///
|
||||
/// For example, `int` has a depth of `0`, `list[int]` has a depth of `1`, and `list[set[int]]`
|
||||
|
|
|
|||
Loading…
Reference in New Issue