diff --git a/crates/ty_python_semantic/src/place.rs b/crates/ty_python_semantic/src/place.rs index 02ccbbac57..5c586884d3 100644 --- a/crates/ty_python_semantic/src/place.rs +++ b/crates/ty_python_semantic/src/place.rs @@ -1671,7 +1671,7 @@ mod implicit_globals { Place::Defined( DefinedPlace::new(KnownClass::Dict.to_specialized_instance( db, - [Type::any(), KnownClass::Int.to_instance(db)], + &[Type::any(), KnownClass::Int.to_instance(db)], )) .with_definedness(Definedness::PossiblyUndefined), ) @@ -1689,7 +1689,7 @@ mod implicit_globals { ), KnownClass::Dict.to_specialized_instance( db, - [KnownClass::Str.to_instance(db), Type::any()], + &[KnownClass::Str.to_instance(db), Type::any()], ), ); Place::Defined( diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 391cbf40f8..4a61de1ba2 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -4127,7 +4127,7 @@ impl<'db> Type<'db> { .with_annotated_type( KnownClass::Dict.to_specialized_instance( db, - [str_instance, Type::any()], + &[str_instance, Type::any()], ), ), ], @@ -4377,7 +4377,7 @@ impl<'db> Type<'db> { .with_annotated_type( KnownClass::Iterable.to_specialized_instance( db, - [Type::TypeVar(element_ty)], + &[Type::TypeVar(element_ty)], ), )], ), @@ -5840,7 +5840,7 @@ impl<'db> Type<'db> { pub(crate) fn dunder_class(self, db: &'db dyn Db) -> Type<'db> { if self.is_typed_dict() { return KnownClass::Dict - .to_specialized_class_type(db, [KnownClass::Str.to_instance(db), Type::object()]) + .to_specialized_class_type(db, &[KnownClass::Str.to_instance(db), Type::object()]) .map(Type::from) // Guard against user-customized typesheds with a broken `dict` class .unwrap_or_else(Type::unknown); @@ -8370,7 +8370,7 @@ impl<'db> BoundTypeVarInstance<'db> { let upper_bound = TypeVarBoundOrConstraints::UpperBound(match kind { ParamSpecAttrKind::Args => Type::homogeneous_tuple(db, Type::object()), ParamSpecAttrKind::Kwargs => KnownClass::Dict - .to_specialized_instance(db, [KnownClass::Str.to_instance(db), Type::any()]) + .to_specialized_instance(db, &[KnownClass::Str.to_instance(db), Type::any()]) .top_materialization(db), }); @@ -13079,11 +13079,11 @@ pub(crate) mod tests { let recursive = UnionType::from_elements( &db, [ - KnownClass::List.to_specialized_instance(&db, [div]), + KnownClass::List.to_specialized_instance(&db, &[div]), Type::none(&db), ], ); - let nested_rec = KnownClass::List.to_specialized_instance(&db, [recursive]); + let nested_rec = KnownClass::List.to_specialized_instance(&db, &[recursive]); assert_eq!( nested_rec.display(&db).to_string(), "list[list[Divergent] | None]" diff --git a/crates/ty_python_semantic/src/types/bound_super.rs b/crates/ty_python_semantic/src/types/bound_super.rs index 56f47a1b20..e4c41e37f1 100644 --- a/crates/ty_python_semantic/src/types/bound_super.rs +++ b/crates/ty_python_semantic/src/types/bound_super.rs @@ -603,7 +603,7 @@ impl<'db> BoundSuperType<'db> { } return delegate_to( KnownClass::Dict - .to_specialized_instance(db, [key_builder.build(), value_builder.build()]), + .to_specialized_instance(db, &[key_builder.build(), value_builder.build()]), ); } Type::NewTypeInstance(newtype) => { diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index 545de4809e..9801fde7b5 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -662,7 +662,7 @@ impl<'db> Bindings<'db> { if let Some(enum_instance) = bound_method.self_instance(db).to_instance(db) { overload.set_return_type( - KnownClass::Iterator.to_specialized_instance(db, [enum_instance]), + KnownClass::Iterator.to_specialized_instance(db, &[enum_instance]), ); } } @@ -993,7 +993,7 @@ impl<'db> Bindings<'db> { let specialization = UnionType::from_elements(db, member_names); overload.set_return_type( KnownClass::FrozenSet - .to_specialized_instance(db, [specialization]), + .to_specialized_instance(db, &[specialization]), ); } } diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index bfbaf704cd..5e0339b53f 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::cell::RefCell; use std::fmt::Write; use std::sync::{LazyLock, Mutex}; @@ -1091,7 +1092,7 @@ impl<'db> ClassType<'db> { let mut tuple_elements = tuple.iter_all_elements(); iterable_parameter = iterable_parameter.with_annotated_type( KnownClass::Iterable - .to_specialized_instance(db, [tuple_elements.next().unwrap()]), + .to_specialized_instance(db, &[tuple_elements.next().unwrap()]), ); assert_eq!( tuple_elements.next(), @@ -2061,7 +2062,7 @@ impl<'db> ClassLiteral<'db> { let name = Type::string_literal(db, self.name(db)); let bases = Type::heterogeneous_tuple(db, self.explicit_bases(db)); let namespace = KnownClass::Dict - .to_specialized_instance(db, [KnownClass::Str.to_instance(db), Type::any()]); + .to_specialized_instance(db, &[KnownClass::Str.to_instance(db), Type::any()]); // TODO: Other keyword arguments? let arguments = CallArguments::positional([name, bases, namespace]); @@ -2308,9 +2309,9 @@ impl<'db> ClassLiteral<'db> { return Member { inner: Place::declared(KnownClass::Dict.to_specialized_instance( db, - [ + &[ KnownClass::Str.to_instance(db), - KnownClass::Field.to_specialized_instance(db, [Type::any()]), + KnownClass::Field.to_specialized_instance(db, &[Type::any()]), ], )) .with_qualifiers(TypeQualifiers::CLASS_VAR), @@ -5197,18 +5198,26 @@ impl KnownClass { /// /// If the class cannot be found in typeshed, or if you provide a specialization with the wrong /// number of types, a debug-level log message will be emitted stating this. - pub(crate) fn to_specialized_class_type<'db>( + pub(crate) fn to_specialized_class_type<'t, 'db, T>( self, db: &'db dyn Db, - specialization: impl IntoIterator>, - ) -> Option> { - fn to_specialized_class_type_impl<'db>( + specialization: T, + ) -> Option> + where + T: Into]>>, + 'db: 't, + { + fn inner<'db>( db: &'db dyn Db, class: KnownClass, - class_literal: ClassLiteral<'db>, - specialization: Box<[Type<'db>]>, - generic_context: GenericContext<'db>, - ) -> ClassType<'db> { + specialization: Cow<[Type<'db>]>, + ) -> Option> { + let Type::ClassLiteral(class_literal) = class.to_class_literal(db) else { + return None; + }; + + let generic_context = class_literal.generic_context(db)?; + if specialization.len() != generic_context.len(db) { // a cache of the `KnownClass`es that we have already seen mismatched-arity // specializations for (and therefore that we've already logged a warning for) @@ -5217,31 +5226,21 @@ impl KnownClass { if MESSAGES.lock().unwrap().insert(class) { tracing::info!( "Wrong number of types when specializing {}. \ - Falling back to default specialization for the symbol instead.", + Falling back to default specialization for the symbol instead.", class.display(db) ); } - return class_literal.default_specialization(db); + return Some(class_literal.default_specialization(db)); } - class_literal - .apply_specialization(db, |_| generic_context.specialize(db, specialization)) + Some( + class_literal + .apply_specialization(db, |_| generic_context.specialize(db, specialization)), + ) } - let Type::ClassLiteral(class_literal) = self.to_class_literal(db) else { - return None; - }; - - let generic_context = class_literal.generic_context(db)?; - let types = specialization.into_iter().collect::>(); - - Some(to_specialized_class_type_impl( - db, - self, - class_literal, - types, - generic_context, - )) + let specialization = specialization.into(); + inner(db, self, specialization) } /// Lookup a [`KnownClass`] in typeshed and return a [`Type`] @@ -5250,11 +5249,15 @@ impl KnownClass { /// If the class cannot be found in typeshed, or if you provide a specialization with the wrong /// number of types, a debug-level log message will be emitted stating this. #[track_caller] - pub(crate) fn to_specialized_instance<'db>( + pub(crate) fn to_specialized_instance<'t, 'db, T>( self, db: &'db dyn Db, - specialization: impl IntoIterator>, - ) -> Type<'db> { + specialization: T, + ) -> Type<'db> + where + T: Into]>>, + 'db: 't, + { debug_assert_ne!( self, KnownClass::Tuple, diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index e813ad3b0b..b92330f729 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::cell::RefCell; use std::collections::hash_map::Entry; use std::fmt::Display; @@ -500,13 +501,17 @@ impl<'db> GenericContext<'db> { /// Returns a specialization of this generic context where each typevar is mapped to itself. pub(crate) fn identity_specialization(self, db: &'db dyn Db) -> Specialization<'db> { - let types = self.variables(db).map(Type::TypeVar).collect(); + let types: Vec = self.variables(db).map(Type::TypeVar).collect(); self.specialize(db, types) } pub(crate) fn unknown_specialization(self, db: &'db dyn Db) -> Specialization<'db> { - let types = vec![Type::unknown(); self.len(db)]; - self.specialize(db, types.into()) + match self.len(db) { + 0 => self.specialize(db, &[]), + 1 => self.specialize(db, &[Type::unknown(); 1]), + 2 => self.specialize(db, &[Type::unknown(); 2]), + len => self.specialize(db, vec![Type::unknown(); len]), + } } pub(crate) fn is_subset_of(self, db: &'db dyn Db, other: GenericContext<'db>) -> bool { @@ -545,11 +550,13 @@ impl<'db> GenericContext<'db> { /// otherwise, you will be left with a partial specialization. (Use /// [`specialize_recursive`](Self::specialize_recursive) if your types might mention typevars /// in this generic context.) - pub(crate) fn specialize( - self, - db: &'db dyn Db, - types: Box<[Type<'db>]>, - ) -> Specialization<'db> { + pub(crate) fn specialize<'t, T>(self, db: &'db dyn Db, types: T) -> Specialization<'db> + where + T: Into]>>, + 'db: 't, + { + let types = types.into(); + assert_eq!(self.len(db), types.len()); Specialization::new(db, self, types, None, None) } diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index 3a6e36914f..ebcf245411 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -2836,7 +2836,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { } KnownClass::Dict.to_specialized_instance( self.db(), - [KnownClass::Str.to_instance(self.db()), Type::unknown()], + &[KnownClass::Str.to_instance(self.db()), Type::unknown()], ) } @@ -2848,14 +2848,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { // The diagnostic for this case is handled in `in_type_expression`. KnownClass::Dict.to_specialized_instance( self.db(), - [KnownClass::Str.to_instance(self.db()), Type::unknown()], + &[KnownClass::Str.to_instance(self.db()), Type::unknown()], ) } } } else { KnownClass::Dict.to_specialized_instance( self.db(), - [KnownClass::Str.to_instance(self.db()), annotated_type], + &[KnownClass::Str.to_instance(self.db()), annotated_type], ) }; self.add_declaration_with_binding( @@ -2866,7 +2866,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { } else { let inferred_ty = KnownClass::Dict.to_specialized_instance( self.db(), - [KnownClass::Str.to_instance(self.db()), Type::unknown()], + &[KnownClass::Str.to_instance(self.db()), Type::unknown()], ); self.add_binding(parameter.into(), definition) @@ -3392,7 +3392,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { } else { KnownClass::BaseExceptionGroup }; - class.to_specialized_instance(self.db(), [symbol_ty]) + class.to_specialized_instance(self.db(), &[symbol_ty]) } else { symbol_ty } @@ -8007,7 +8007,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { let infer_elt_ty = |builder: &mut Self, elt, tcx| builder.infer_expression(elt, tcx); self.infer_collection_literal(elts, tcx, infer_elt_ty, KnownClass::List) .unwrap_or_else(|| { - KnownClass::List.to_specialized_instance(self.db(), [Type::unknown()]) + KnownClass::List.to_specialized_instance(self.db(), &[Type::unknown()]) }) } @@ -8022,7 +8022,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { let infer_elt_ty = |builder: &mut Self, elt, tcx| builder.infer_expression(elt, tcx); self.infer_collection_literal(elts, tcx, infer_elt_ty, KnownClass::Set) .unwrap_or_else(|| { - KnownClass::Set.to_specialized_instance(self.db(), [Type::unknown()]) + KnownClass::Set.to_specialized_instance(self.db(), &[Type::unknown()]) }) } @@ -8049,7 +8049,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { // unsupported. if let Some(Type::Dynamic(DynamicType::Todo(_))) = tcx.annotation { return KnownClass::Dict - .to_specialized_instance(self.db(), [Type::unknown(), Type::unknown()]); + .to_specialized_instance(self.db(), &[Type::unknown(), Type::unknown()]); } let items = items @@ -8069,7 +8069,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { self.infer_collection_literal(items, tcx, infer_elt_ty, KnownClass::Dict) .unwrap_or_else(|| { KnownClass::Dict - .to_specialized_instance(self.db(), [Type::unknown(), Type::unknown()]) + .to_specialized_instance(self.db(), &[Type::unknown(), Type::unknown()]) }) } @@ -8339,11 +8339,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { if evaluation_mode.is_async() { KnownClass::AsyncGeneratorType - .to_specialized_instance(self.db(), [yield_type, Type::none(self.db())]) + .to_specialized_instance(self.db(), &[yield_type, Type::none(self.db())]) } else { KnownClass::GeneratorType.to_specialized_instance( self.db(), - [yield_type, Type::none(self.db()), Type::none(self.db())], + &[yield_type, Type::none(self.db()), Type::none(self.db())], ) } } @@ -8373,20 +8373,22 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { .zip(inferred_element_types.iter()) .all(|(annotated, inferred)| inferred.is_assignable_to(self.db(), *annotated)) { - collection_class - .to_specialized_instance(self.db(), annotated_element_types.iter().copied()) + collection_class.to_specialized_instance(self.db(), annotated_element_types) } else { collection_class.to_specialized_instance( self.db(), - inferred_element_types.iter().map(|ty| { - UnionType::from_elements( - self.db(), - [ - ty.promote_literals(self.db(), TypeContext::default()), - Type::unknown(), - ], - ) - }), + inferred_element_types + .iter() + .map(|ty| { + UnionType::from_elements( + self.db(), + [ + ty.promote_literals(self.db(), TypeContext::default()), + Type::unknown(), + ], + ) + }) + .collect::>(), ) } } @@ -12195,7 +12197,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { .expect("A known stdlib class is available"); return class - .to_specialized_class_type(self.db(), [element_ty]) + .to_specialized_class_type(self.db(), &[element_ty]) .map(Type::from) .unwrap_or_else(Type::unknown); } @@ -12253,7 +12255,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { .expect("Stdlib class available"); return class - .to_specialized_class_type(self.db(), [first_ty, second_ty]) + .to_specialized_class_type(self.db(), &[first_ty, second_ty]) .map(Type::from) .unwrap_or_else(Type::unknown); } @@ -13115,7 +13117,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { type_to_slice_argument(ty_step), ) { (SliceArg::Arg(lower), SliceArg::Arg(upper), SliceArg::Arg(step)) => { - KnownClass::Slice.to_specialized_instance(self.db(), [lower, upper, step]) + KnownClass::Slice.to_specialized_instance(self.db(), &[lower, upper, step]) } _ => KnownClass::Slice.to_instance(self.db()), } diff --git a/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs b/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs index e56cf0feea..cd1d53c114 100644 --- a/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs +++ b/crates/ty_python_semantic/src/types/infer/builder/type_expression.rs @@ -793,7 +793,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> { todo_type!("specialized recursive generic type alias"), generic_context.len(db), ) - .collect(), + .collect::>(), ), ); return if in_type_expression { @@ -1131,7 +1131,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> { } let ty = class.to_specialized_instance( self.db(), - args.iter().map(|node| self.infer_type_expression(node)), + args.iter() + .map(|node| self.infer_type_expression(node)) + .collect::>(), ); if arguments.is_tuple_expr() { self.store_expression_type(arguments, ty); diff --git a/crates/ty_python_semantic/src/types/property_tests.rs b/crates/ty_python_semantic/src/types/property_tests.rs index 4ac29c25c2..f5a20aa69c 100644 --- a/crates/ty_python_semantic/src/types/property_tests.rs +++ b/crates/ty_python_semantic/src/types/property_tests.rs @@ -235,7 +235,7 @@ mod stable { // `Iterable` but assigns `__iter__ = None` in the class body (or similar). type_property_test!( all_types_assignable_to_iterable_are_iterable, db, - forall types t. t.is_assignable_to(db, KnownClass::Iterable.to_specialized_instance(db, [Type::object()])) => t.try_iterate(db).is_ok() + forall types t. t.is_assignable_to(db, KnownClass::Iterable.to_specialized_instance(db, &[Type::object()])) => t.try_iterate(db).is_ok() ); // Our optimized `Type::negate()` function should always produce the exact same type diff --git a/crates/ty_python_semantic/src/types/relation.rs b/crates/ty_python_semantic/src/types/relation.rs index 3a617fe035..45c30e5bef 100644 --- a/crates/ty_python_semantic/src/types/relation.rs +++ b/crates/ty_python_semantic/src/types/relation.rs @@ -1015,7 +1015,7 @@ impl<'db> Type<'db> { // key types are a supertype of the extra items type?) (Type::TypedDict(_), _) => relation_visitor.visit((self, target, relation), || { KnownClass::Mapping - .to_specialized_instance(db, [KnownClass::Str.to_instance(db), Type::object()]) + .to_specialized_instance(db, &[KnownClass::Str.to_instance(db), Type::object()]) .has_relation_to_impl( db, target, @@ -2384,7 +2384,7 @@ impl<'db> Type<'db> { // `dict` *itself* is almost always disjoint from `TypedDict` -- but it's a good // approximation, and some false negatives are acceptable. (Type::TypedDict(_), other) | (other, Type::TypedDict(_)) => KnownClass::Dict - .to_specialized_instance(db, [KnownClass::Str.to_instance(db), Type::any()]) + .to_specialized_instance(db, &[KnownClass::Str.to_instance(db), Type::any()]) .has_relation_to_impl( db, other, diff --git a/crates/ty_python_semantic/src/types/signatures.rs b/crates/ty_python_semantic/src/types/signatures.rs index 6d982c0cf6..ce53f143ff 100644 --- a/crates/ty_python_semantic/src/types/signatures.rs +++ b/crates/ty_python_semantic/src/types/signatures.rs @@ -699,7 +699,7 @@ impl<'db> Signature<'db> { pub(super) fn wrap_coroutine_return_type(self, db: &'db dyn Db) -> Self { let return_ty = KnownClass::CoroutineType - .to_specialized_instance(db, [Type::any(), Type::any(), self.return_ty]); + .to_specialized_instance(db, &[Type::any(), Type::any(), self.return_ty]); Self { return_ty, ..self } } diff --git a/crates/ty_python_semantic/src/types/tuple.rs b/crates/ty_python_semantic/src/types/tuple.rs index 29fc866362..dfc484cabd 100644 --- a/crates/ty_python_semantic/src/types/tuple.rs +++ b/crates/ty_python_semantic/src/types/tuple.rs @@ -1898,7 +1898,7 @@ impl<'db> TupleUnpacker<'db> { .into_all_elements_with_kind() .map(|builder| match builder { TupleElement::Variable(builder) => builder.try_build().unwrap_or_else(|| { - KnownClass::List.to_specialized_instance(self.db, [Type::unknown()]) + KnownClass::List.to_specialized_instance(self.db, &[Type::unknown()]) }), TupleElement::Fixed(builder) | TupleElement::Prefix(builder) @@ -1927,7 +1927,7 @@ impl<'db> VariableLengthTuple> { target.add_in_place(value); } self.variable_element_mut() - .add_in_place(KnownClass::List.to_specialized_instance(db, [values.variable()])); + .add_in_place(KnownClass::List.to_specialized_instance(db, &[values.variable()])); for (target, value) in (self.suffix_elements_mut().iter_mut()).zip(values.iter_suffix_elements()) {