diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index c6d85c037d..d52b909115 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -53,7 +53,6 @@ use crate::types::function::{ }; use crate::types::generics::{ GenericContext, PartialSpecialization, Specialization, bind_typevar, walk_generic_context, - walk_partial_specialization, walk_specialization, }; pub use crate::types::ide_support::{ CallSignatureDetails, Member, MemberWithDefinition, all_members, call_signature_details, @@ -6109,7 +6108,7 @@ impl<'db> Type<'db> { } Type::FunctionLiteral(function) => { - let function = Type::FunctionLiteral(function.with_type_mapping(db, type_mapping)); + let function = Type::FunctionLiteral(function.apply_type_mapping_impl(db, type_mapping, visitor)); match type_mapping { TypeMapping::PromoteLiterals => function.literal_promotion_type(db) @@ -6120,8 +6119,8 @@ impl<'db> Type<'db> { Type::BoundMethod(method) => Type::BoundMethod(BoundMethodType::new( db, - method.function(db).with_type_mapping(db, type_mapping), - method.self_instance(db).apply_type_mapping(db, type_mapping), + method.function(db).apply_type_mapping_impl(db, type_mapping, visitor), + method.self_instance(db).apply_type_mapping_impl(db, type_mapping, visitor), )), Type::NominalInstance(instance) => @@ -6140,13 +6139,13 @@ impl<'db> Type<'db> { Type::KnownBoundMethod(KnownBoundMethodType::FunctionTypeDunderGet(function)) => { Type::KnownBoundMethod(KnownBoundMethodType::FunctionTypeDunderGet( - function.with_type_mapping(db, type_mapping), + function.apply_type_mapping_impl(db, type_mapping, visitor), )) } Type::KnownBoundMethod(KnownBoundMethodType::FunctionTypeDunderCall(function)) => { Type::KnownBoundMethod(KnownBoundMethodType::FunctionTypeDunderCall( - function.with_type_mapping(db, type_mapping), + function.apply_type_mapping_impl(db, type_mapping, visitor), )) } @@ -6782,84 +6781,7 @@ pub enum TypeMapping<'a, 'db> { Materialize(MaterializationKind), } -fn walk_type_mapping<'db, V: visitor::TypeVisitor<'db> + ?Sized>( - db: &'db dyn Db, - mapping: &TypeMapping<'_, 'db>, - visitor: &V, -) { - match mapping { - TypeMapping::Specialization(specialization) => { - walk_specialization(db, *specialization, visitor); - } - TypeMapping::PartialSpecialization(specialization) => { - walk_partial_specialization(db, specialization, visitor); - } - TypeMapping::BindSelf(self_type) => { - visitor.visit_type(db, *self_type); - } - TypeMapping::ReplaceSelf { new_upper_bound } => { - visitor.visit_type(db, *new_upper_bound); - } - TypeMapping::PromoteLiterals - | TypeMapping::BindLegacyTypevars(_) - | TypeMapping::MarkTypeVarsInferable(_) - | TypeMapping::Materialize(_) => {} - } -} - impl<'db> TypeMapping<'_, 'db> { - fn to_owned(&self) -> TypeMapping<'db, 'db> { - match self { - TypeMapping::Specialization(specialization) => { - TypeMapping::Specialization(*specialization) - } - TypeMapping::PartialSpecialization(partial) => { - TypeMapping::PartialSpecialization(partial.to_owned()) - } - TypeMapping::PromoteLiterals => TypeMapping::PromoteLiterals, - TypeMapping::BindLegacyTypevars(binding_context) => { - TypeMapping::BindLegacyTypevars(*binding_context) - } - TypeMapping::BindSelf(self_type) => TypeMapping::BindSelf(*self_type), - TypeMapping::ReplaceSelf { new_upper_bound } => TypeMapping::ReplaceSelf { - new_upper_bound: *new_upper_bound, - }, - TypeMapping::MarkTypeVarsInferable(binding_context) => { - TypeMapping::MarkTypeVarsInferable(*binding_context) - } - TypeMapping::Materialize(materialization_kind) => { - TypeMapping::Materialize(*materialization_kind) - } - } - } - - fn normalized_impl(&self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self { - match self { - TypeMapping::Specialization(specialization) => { - TypeMapping::Specialization(specialization.normalized_impl(db, visitor)) - } - TypeMapping::PartialSpecialization(partial) => { - TypeMapping::PartialSpecialization(partial.normalized_impl(db, visitor)) - } - TypeMapping::PromoteLiterals => TypeMapping::PromoteLiterals, - TypeMapping::BindLegacyTypevars(binding_context) => { - TypeMapping::BindLegacyTypevars(*binding_context) - } - TypeMapping::BindSelf(self_type) => { - TypeMapping::BindSelf(self_type.normalized_impl(db, visitor)) - } - TypeMapping::ReplaceSelf { new_upper_bound } => TypeMapping::ReplaceSelf { - new_upper_bound: new_upper_bound.normalized_impl(db, visitor), - }, - TypeMapping::MarkTypeVarsInferable(binding_context) => { - TypeMapping::MarkTypeVarsInferable(*binding_context) - } - TypeMapping::Materialize(materialization_kind) => { - TypeMapping::Materialize(*materialization_kind) - } - } - } - /// Update the generic context of a [`Signature`] according to the current type mapping pub(crate) fn update_signature_generic_context( &self, diff --git a/crates/ty_python_semantic/src/types/function.rs b/crates/ty_python_semantic/src/types/function.rs index ed10c7b819..c062ee3b1d 100644 --- a/crates/ty_python_semantic/src/types/function.rs +++ b/crates/ty_python_semantic/src/types/function.rs @@ -77,11 +77,11 @@ use crate::types::narrow::ClassInfoConstraintFunction; use crate::types::signatures::{CallableSignature, Signature}; use crate::types::visitor::any_over_type; use crate::types::{ - BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase, ClassLiteral, ClassType, - DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor, HasRelationToVisitor, - IsEquivalentVisitor, KnownClass, KnownInstanceType, NormalizedVisitor, SpecialFormType, - TrackedConstraintSet, Truthiness, Type, TypeMapping, TypeRelation, UnionBuilder, all_members, - binding_type, todo_type, walk_type_mapping, + ApplyTypeMappingVisitor, BoundMethodType, BoundTypeVarInstance, CallableType, ClassBase, + ClassLiteral, ClassType, DeprecatedInstance, DynamicType, FindLegacyTypeVarsVisitor, + HasRelationToVisitor, IsEquivalentVisitor, KnownClass, KnownInstanceType, NormalizedVisitor, + SpecialFormType, TrackedConstraintSet, Truthiness, Type, TypeMapping, TypeRelation, + UnionBuilder, all_members, binding_type, todo_type, walk_signature, }; use crate::{Db, FxOrderSet, ModuleName, resolve_module}; @@ -623,33 +623,24 @@ impl<'db> FunctionLiteral<'db> { /// calling query is not in the same file as this function is defined in, then this will create /// a cross-module dependency directly on the full AST which will lead to cache /// over-invalidation. - fn signature<'a>( - self, - db: &'db dyn Db, - type_mappings: &'a [TypeMapping<'a, 'db>], - ) -> CallableSignature<'db> - where - 'db: 'a, - { + fn signature(self, db: &'db dyn Db) -> CallableSignature<'db> { // We only include an implementation (i.e. a definition not decorated with `@overload`) if // it's the only definition. let inherited_generic_context = self.inherited_generic_context(db); let (overloads, implementation) = self.overloads_and_implementation(db); if let Some(implementation) = implementation { if overloads.is_empty() { - return CallableSignature::single(type_mappings.iter().fold( + return CallableSignature::single( implementation.signature(db, inherited_generic_context), - |sig, mapping| sig.apply_type_mapping(db, mapping), - )); + ); } } - CallableSignature::from_overloads(overloads.iter().map(|overload| { - type_mappings.iter().fold( - overload.signature(db, inherited_generic_context), - |sig, mapping| sig.apply_type_mapping(db, mapping), - ) - })) + CallableSignature::from_overloads( + overloads + .iter() + .map(|overload| overload.signature(db, inherited_generic_context)), + ) } /// Typed externally-visible signature of the last overload or implementation of this function. @@ -660,20 +651,10 @@ impl<'db> FunctionLiteral<'db> { /// calling query is not in the same file as this function is defined in, then this will create /// a cross-module dependency directly on the full AST which will lead to cache /// over-invalidation. - fn last_definition_signature<'a>( - self, - db: &'db dyn Db, - type_mappings: &'a [TypeMapping<'a, 'db>], - ) -> Signature<'db> - where - 'db: 'a, - { + fn last_definition_signature(self, db: &'db dyn Db) -> Signature<'db> { let inherited_generic_context = self.inherited_generic_context(db); - type_mappings.iter().fold( - self.last_definition(db) - .signature(db, inherited_generic_context), - |sig, mapping| sig.apply_type_mapping(db, mapping), - ) + self.last_definition(db) + .signature(db, inherited_generic_context) } fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self { @@ -691,11 +672,19 @@ impl<'db> FunctionLiteral<'db> { pub struct FunctionType<'db> { pub(crate) literal: FunctionLiteral<'db>, - /// Type mappings that should be applied to the function's parameter and return types. This - /// might include specializations of enclosing generic contexts (e.g. for non-generic methods - /// of a specialized generic class). - #[returns(deref)] - type_mappings: Box<[TypeMapping<'db, 'db>]>, + /// Contains a potentially modified signature for this function literal, in case certain operations + /// (like type mappings) have been applied to it. + /// + /// See also: [`FunctionLiteral::updated_signature`]. + #[returns(as_ref)] + updated_signature: Option>, + + /// Contains a potentially modified signature for the last overload or the implementation of this + /// function literal, in case certain operations (like type mappings) have been applied to it. + /// + /// See also: [`FunctionLiteral::last_definition_signature`]. + #[returns(as_ref)] + updated_last_definition_signature: Option>, } // The Salsa heap is tracked separately. @@ -707,8 +696,13 @@ pub(super) fn walk_function_type<'db, V: super::visitor::TypeVisitor<'db> + ?Siz visitor: &V, ) { walk_function_literal(db, function.literal(db), visitor); - for mapping in function.type_mappings(db) { - walk_type_mapping(db, mapping, visitor); + if let Some(callable_signature) = function.updated_signature(db) { + for signature in &callable_signature.overloads { + walk_signature(db, signature, visitor); + } + } + if let Some(signature) = function.updated_last_definition_signature(db) { + walk_signature(db, signature, visitor); } } @@ -722,21 +716,41 @@ impl<'db> FunctionType<'db> { let literal = self .literal(db) .with_inherited_generic_context(db, inherited_generic_context); - Self::new(db, literal, self.type_mappings(db)) + let updated_signature = self.updated_signature(db).map(|signature| { + signature.with_inherited_generic_context(Some(inherited_generic_context)) + }); + let updated_last_definition_signature = + self.updated_last_definition_signature(db).map(|signature| { + signature + .clone() + .with_inherited_generic_context(Some(inherited_generic_context)) + }); + Self::new( + db, + literal, + updated_signature, + updated_last_definition_signature, + ) } - pub(crate) fn with_type_mapping<'a>( + pub(crate) fn apply_type_mapping_impl<'a>( self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>, + visitor: &ApplyTypeMappingVisitor<'db>, ) -> Self { - let type_mappings: Box<[_]> = self - .type_mappings(db) - .iter() - .cloned() - .chain(std::iter::once(type_mapping.to_owned())) - .collect(); - Self::new(db, self.literal(db), type_mappings) + let updated_signature = + self.signature(db) + .apply_type_mapping_impl(db, type_mapping, visitor); + let updated_last_definition_signature = self + .last_definition_signature(db) + .apply_type_mapping_impl(db, type_mapping, visitor); + Self::new( + db, + self.literal(db), + Some(updated_signature), + Some(updated_last_definition_signature), + ) } pub(crate) fn with_dataclass_transformer_params( @@ -752,7 +766,7 @@ impl<'db> FunctionType<'db> { .with_dataclass_transformer_params(db, params); let literal = FunctionLiteral::new(db, last_definition, literal.inherited_generic_context(db)); - Self::new(db, literal, self.type_mappings(db)) + Self::new(db, literal, None, None) } /// Returns the [`File`] in which this function is defined. @@ -907,7 +921,9 @@ impl<'db> FunctionType<'db> { /// would depend on the function's AST and rerun for every change in that file. #[salsa::tracked(returns(ref), cycle_fn=signature_cycle_recover, cycle_initial=signature_cycle_initial, heap_size=ruff_memory_usage::heap_size)] pub(crate) fn signature(self, db: &'db dyn Db) -> CallableSignature<'db> { - self.literal(db).signature(db, self.type_mappings(db)) + self.updated_signature(db) + .cloned() + .unwrap_or_else(|| self.literal(db).signature(db)) } /// Typed externally-visible signature of the last overload or implementation of this function. @@ -926,8 +942,9 @@ impl<'db> FunctionType<'db> { heap_size=ruff_memory_usage::heap_size, )] pub(crate) fn last_definition_signature(self, db: &'db dyn Db) -> Signature<'db> { - self.literal(db) - .last_definition_signature(db, self.type_mappings(db)) + self.updated_last_definition_signature(db) + .cloned() + .unwrap_or_else(|| self.literal(db).last_definition_signature(db)) } /// Convert the `FunctionType` into a [`CallableType`]. @@ -1017,12 +1034,19 @@ impl<'db> FunctionType<'db> { } pub(crate) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self { - let mappings: Box<_> = self - .type_mappings(db) - .iter() - .map(|mapping| mapping.normalized_impl(db, visitor)) - .collect(); - Self::new(db, self.literal(db).normalized_impl(db, visitor), mappings) + let literal = self.literal(db).normalized_impl(db, visitor); + let updated_signature = self + .updated_signature(db) + .map(|signature| signature.normalized_impl(db, visitor)); + let updated_last_definition_signature = self + .updated_last_definition_signature(db) + .map(|signature| signature.normalized_impl(db, visitor)); + Self::new( + db, + literal, + updated_signature, + updated_last_definition_signature, + ) } } diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index 3eb227b647..c781d4a69e 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use crate::types::constraints::ConstraintSet; use itertools::Itertools; @@ -392,7 +390,7 @@ impl<'db> GenericContext<'db> { // requirement for legacy contexts.) let partial = PartialSpecialization { generic_context: self, - types: Cow::Borrowed(&expanded[0..idx]), + types: &expanded[0..idx], }; let default = default.apply_type_mapping(db, &TypeMapping::PartialSpecialization(partial)); @@ -947,18 +945,7 @@ impl<'db> Specialization<'db> { #[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)] pub struct PartialSpecialization<'a, 'db> { generic_context: GenericContext<'db>, - types: Cow<'a, [Type<'db>]>, -} - -pub(super) fn walk_partial_specialization<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>( - db: &'db dyn Db, - specialization: &PartialSpecialization<'_, 'db>, - visitor: &V, -) { - walk_generic_context(db, specialization.generic_context, visitor); - for ty in &*specialization.types { - visitor.visit_type(db, *ty); - } + types: &'a [Type<'db>], } impl<'db> PartialSpecialization<'_, 'db> { @@ -975,31 +962,6 @@ impl<'db> PartialSpecialization<'_, 'db> { .get_index_of(&bound_typevar)?; self.types.get(index).copied() } - - pub(crate) fn to_owned(&self) -> PartialSpecialization<'db, 'db> { - PartialSpecialization { - generic_context: self.generic_context, - types: Cow::from(self.types.clone().into_owned()), - } - } - - pub(crate) fn normalized_impl( - &self, - db: &'db dyn Db, - visitor: &NormalizedVisitor<'db>, - ) -> PartialSpecialization<'db, 'db> { - let generic_context = self.generic_context.normalized_impl(db, visitor); - let types: Cow<_> = self - .types - .iter() - .map(|ty| ty.normalized_impl(db, visitor)) - .collect(); - - PartialSpecialization { - generic_context, - types, - } - } } /// Performs type inference between parameter annotations and argument types, producing a diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index 79cd816b0a..f7eb128766 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -2144,12 +2144,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { let function_literal = FunctionLiteral::new(self.db(), overload_literal, inherited_generic_context); - let type_mappings = Box::default(); - let mut inferred_ty = Type::FunctionLiteral(FunctionType::new( - self.db(), - function_literal, - type_mappings, - )); + let mut inferred_ty = + Type::FunctionLiteral(FunctionType::new(self.db(), function_literal, None, None)); self.undecorated_type = Some(inferred_ty); for (decorator_ty, decorator_node) in decorator_types_and_nodes.iter().rev() { diff --git a/crates/ty_python_semantic/src/types/signatures.rs b/crates/ty_python_semantic/src/types/signatures.rs index d5ae64fdaf..3d3868b06c 100644 --- a/crates/ty_python_semantic/src/types/signatures.rs +++ b/crates/ty_python_semantic/src/types/signatures.rs @@ -62,6 +62,17 @@ impl<'db> CallableSignature<'db> { self.overloads.iter() } + pub(crate) fn with_inherited_generic_context( + &self, + inherited_generic_context: Option>, + ) -> Self { + Self::from_overloads(self.overloads.iter().map(|signature| { + signature + .clone() + .with_inherited_generic_context(inherited_generic_context) + })) + } + pub(crate) fn normalized_impl( &self, db: &'db dyn Db, @@ -451,14 +462,6 @@ impl<'db> Signature<'db> { } } - pub(crate) fn apply_type_mapping<'a>( - &self, - db: &'db dyn Db, - type_mapping: &TypeMapping<'a, 'db>, - ) -> Self { - self.apply_type_mapping_impl(db, type_mapping, &ApplyTypeMappingVisitor::default()) - } - pub(crate) fn apply_type_mapping_impl<'a>( &self, db: &'db dyn Db,