diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index debb4d14eb..a002d0f9ae 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -2649,10 +2649,7 @@ impl<'db> Type<'db> { if let Symbol::Type(descr_get, descr_get_boundness) = descr_get { let return_ty = descr_get - .try_call( - db, - &mut CallArgumentTypes::positional([self, instance, owner]), - ) + .try_call(db, &CallArgumentTypes::positional([self, instance, owner])) .map(|bindings| { if descr_get_boundness == Boundness::Bound { bindings.return_type(db) @@ -4207,7 +4204,7 @@ impl<'db> Type<'db> { fn try_call( self, db: &'db dyn Db, - argument_types: &mut CallArgumentTypes<'_, 'db>, + argument_types: &CallArgumentTypes<'_, 'db>, ) -> Result, CallError<'db>> { let signatures = self.signatures(db); Bindings::match_parameters(signatures, argument_types).check_types(db, argument_types) @@ -4420,7 +4417,7 @@ impl<'db> Type<'db> { fn try_call_constructor( self, db: &'db dyn Db, - mut argument_types: CallArgumentTypes<'_, 'db>, + argument_types: CallArgumentTypes<'_, 'db>, ) -> Result, ConstructorCallError<'db>> { debug_assert!(matches!( self, @@ -4486,7 +4483,7 @@ impl<'db> Type<'db> { match new_method { Symbol::Type(new_method, boundness) => { - let result = new_method.try_call(db, argument_types); + let result = new_method.try_call(db, &argument_types); if boundness == Boundness::PossiblyUnbound { return Some(Err(DunderNewCallError::PossiblyUnbound(result.err()))); diff --git a/crates/red_knot_python_semantic/src/types/call/arguments.rs b/crates/red_knot_python_semantic/src/types/call/arguments.rs index cce0c81c0b..df4408121a 100644 --- a/crates/red_knot_python_semantic/src/types/call/arguments.rs +++ b/crates/red_knot_python_semantic/src/types/call/arguments.rs @@ -11,16 +11,18 @@ impl<'a> CallArguments<'a> { /// Invoke a function with an optional extra synthetic argument (for a `self` or `cls` /// parameter) prepended to the front of this argument list. (If `bound_self` is none, the /// function is invoked with the unmodified argument list.) - pub(crate) fn with_self(&mut self, bound_self: Option>, f: F) -> R + pub(crate) fn with_self(&self, bound_self: Option>, f: F) -> R where - F: FnOnce(&mut Self) -> R, + F: FnOnce(&Self) -> R, { + let mut call_arguments = self.clone(); + if bound_self.is_some() { - self.0.push_front(Argument::Synthetic); + call_arguments.0.push_front(Argument::Synthetic); } - let result = f(self); + let result = f(&call_arguments); if bound_self.is_some() { - self.0.pop_front(); + call_arguments.0.pop_front(); } result } @@ -55,6 +57,7 @@ pub(crate) enum Argument<'a> { } /// Arguments for a single call, in source order, along with inferred types for each argument. +#[derive(Clone, Debug)] pub(crate) struct CallArgumentTypes<'a, 'db> { arguments: CallArguments<'a>, types: VecDeque>, @@ -93,20 +96,20 @@ impl<'a, 'db> CallArgumentTypes<'a, 'db> { /// Invoke a function with an optional extra synthetic argument (for a `self` or `cls` /// parameter) prepended to the front of this argument list. (If `bound_self` is none, the /// function is invoked with the unmodified argument list.) - pub(crate) fn with_self(&mut self, bound_self: Option>, f: F) -> R + pub(crate) fn with_self(&self, bound_self: Option>, f: F) -> R where - F: FnOnce(&mut Self) -> R, + F: FnOnce(Self) -> R, { + let mut call_argument_types = self.clone(); + if let Some(bound_self) = bound_self { - self.arguments.0.push_front(Argument::Synthetic); - self.types.push_front(bound_self); + call_argument_types + .arguments + .0 + .push_front(Argument::Synthetic); + call_argument_types.types.push_front(bound_self); } - let result = f(self); - if bound_self.is_some() { - self.arguments.0.pop_front(); - self.types.pop_front(); - } - result + f(call_argument_types) } pub(crate) fn iter(&self) -> impl Iterator, Type<'db>)> + '_ { diff --git a/crates/red_knot_python_semantic/src/types/call/bind.rs b/crates/red_knot_python_semantic/src/types/call/bind.rs index 3bac167755..1af29eb62e 100644 --- a/crates/red_knot_python_semantic/src/types/call/bind.rs +++ b/crates/red_knot_python_semantic/src/types/call/bind.rs @@ -56,7 +56,7 @@ impl<'db> Bindings<'db> { /// verify that each argument type is assignable to the corresponding parameter type. pub(crate) fn match_parameters( signatures: Signatures<'db>, - arguments: &mut CallArguments<'_>, + arguments: &CallArguments<'_>, ) -> Self { let mut argument_forms = vec![None; arguments.len()]; let mut conflicting_forms = vec![false; arguments.len()]; @@ -92,7 +92,7 @@ impl<'db> Bindings<'db> { pub(crate) fn check_types( mut self, db: &'db dyn Db, - argument_types: &mut CallArgumentTypes<'_, 'db>, + argument_types: &CallArgumentTypes<'_, 'db>, ) -> Result> { for (signature, element) in self.signatures.iter().zip(&mut self.elements) { element.check_types(db, signature, argument_types); @@ -351,10 +351,7 @@ impl<'db> Bindings<'db> { [Some(Type::PropertyInstance(property)), Some(instance), ..] => { if let Some(getter) = property.getter(db) { if let Ok(return_ty) = getter - .try_call( - db, - &mut CallArgumentTypes::positional([*instance]), - ) + .try_call(db, &CallArgumentTypes::positional([*instance])) .map(|binding| binding.return_type(db)) { overload.set_return_type(return_ty); @@ -383,10 +380,7 @@ impl<'db> Bindings<'db> { [Some(instance), ..] => { if let Some(getter) = property.getter(db) { if let Ok(return_ty) = getter - .try_call( - db, - &mut CallArgumentTypes::positional([*instance]), - ) + .try_call(db, &CallArgumentTypes::positional([*instance])) .map(|binding| binding.return_type(db)) { overload.set_return_type(return_ty); @@ -414,7 +408,7 @@ impl<'db> Bindings<'db> { if let Some(setter) = property.setter(db) { if let Err(_call_error) = setter.try_call( db, - &mut CallArgumentTypes::positional([*instance, *value]), + &CallArgumentTypes::positional([*instance, *value]), ) { overload.errors.push(BindingError::InternalCallError( "calling the setter failed", @@ -433,7 +427,7 @@ impl<'db> Bindings<'db> { if let Some(setter) = property.setter(db) { if let Err(_call_error) = setter.try_call( db, - &mut CallArgumentTypes::positional([*instance, *value]), + &CallArgumentTypes::positional([*instance, *value]), ) { overload.errors.push(BindingError::InternalCallError( "calling the setter failed", @@ -874,7 +868,7 @@ pub(crate) struct CallableBinding<'db> { impl<'db> CallableBinding<'db> { fn match_parameters( signature: &CallableSignature<'db>, - arguments: &mut CallArguments<'_>, + arguments: &CallArguments<'_>, argument_forms: &mut [Option], conflicting_forms: &mut [bool], ) -> Self { @@ -912,13 +906,13 @@ impl<'db> CallableBinding<'db> { &mut self, db: &'db dyn Db, signature: &CallableSignature<'db>, - argument_types: &mut CallArgumentTypes<'_, 'db>, + argument_types: &CallArgumentTypes<'_, 'db>, ) { // If this callable is a bound method, prepend the self instance onto the arguments list // before checking. argument_types.with_self(signature.bound_type, |argument_types| { for (signature, overload) in signature.iter().zip(&mut self.overloads) { - overload.check_types(db, signature, argument_types); + overload.check_types(db, signature, &argument_types); } }); } diff --git a/crates/red_knot_python_semantic/src/types/class.rs b/crates/red_knot_python_semantic/src/types/class.rs index 67a7d0800d..d118d28af2 100644 --- a/crates/red_knot_python_semantic/src/types/class.rs +++ b/crates/red_knot_python_semantic/src/types/class.rs @@ -772,9 +772,9 @@ impl<'db> ClassLiteral<'db> { let namespace = KnownClass::Dict.to_instance(db); // TODO: Other keyword arguments? - let mut arguments = CallArgumentTypes::positional([name, bases, namespace]); + let arguments = CallArgumentTypes::positional([name, bases, namespace]); - let return_ty_result = match metaclass.try_call(db, &mut arguments) { + let return_ty_result = match metaclass.try_call(db, &arguments) { Ok(bindings) => Ok(bindings.return_type(db)), Err(CallError(CallErrorKind::NotCallable, bindings)) => Err(MetaclassError { diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index 74fd48d2e7..991eb960da 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -1815,7 +1815,7 @@ impl<'db> TypeInferenceBuilder<'db> { for (decorator_ty, decorator_node) in decorator_types_and_nodes.iter().rev() { inferred_ty = match decorator_ty - .try_call(self.db(), &mut CallArgumentTypes::positional([inferred_ty])) + .try_call(self.db(), &CallArgumentTypes::positional([inferred_ty])) .map(|bindings| bindings.return_type(self.db())) { Ok(return_ty) => return_ty, @@ -2832,7 +2832,7 @@ impl<'db> TypeInferenceBuilder<'db> { let successful_call = meta_dunder_set .try_call( db, - &mut CallArgumentTypes::positional([ + &CallArgumentTypes::positional([ meta_attr_ty, object_ty, value_ty, @@ -2973,7 +2973,7 @@ impl<'db> TypeInferenceBuilder<'db> { let successful_call = meta_dunder_set .try_call( db, - &mut CallArgumentTypes::positional([ + &CallArgumentTypes::positional([ meta_attr_ty, object_ty, value_ty, @@ -4561,7 +4561,7 @@ impl<'db> TypeInferenceBuilder<'db> { // We don't call `Type::try_call`, because we want to perform type inference on the // arguments after matching them to parameters, but before checking that the argument types // are assignable to any parameter annotations. - let mut call_arguments = Self::parse_arguments(arguments); + let call_arguments = Self::parse_arguments(arguments); let callable_type = self.infer_expression(func); if let Type::FunctionLiteral(function) = callable_type { @@ -4640,11 +4640,11 @@ impl<'db> TypeInferenceBuilder<'db> { } let signatures = callable_type.signatures(self.db()); - let bindings = Bindings::match_parameters(signatures, &mut call_arguments); - let mut call_argument_types = + let bindings = Bindings::match_parameters(signatures, &call_arguments); + let call_argument_types = self.infer_argument_types(arguments, call_arguments, &bindings.argument_forms); - match bindings.check_types(self.db(), &mut call_argument_types) { + match bindings.check_types(self.db(), &call_argument_types) { Ok(mut bindings) => { for binding in &mut bindings { let binding_type = binding.callable_type; @@ -6486,7 +6486,7 @@ impl<'db> TypeInferenceBuilder<'db> { Symbol::Type(contains_dunder, Boundness::Bound) => { // If `__contains__` is available, it is used directly for the membership test. contains_dunder - .try_call(db, &mut CallArgumentTypes::positional([right, left])) + .try_call(db, &CallArgumentTypes::positional([right, left])) .map(|bindings| bindings.return_type(db)) .ok() } @@ -6640,7 +6640,7 @@ impl<'db> TypeInferenceBuilder<'db> { generic_context: GenericContext<'db>, ) -> Type<'db> { let slice_node = subscript.slice.as_ref(); - let mut call_argument_types = match slice_node { + let call_argument_types = match slice_node { ast::Expr::Tuple(tuple) => CallArgumentTypes::positional( tuple.elts.iter().map(|elt| self.infer_type_expression(elt)), ), @@ -6650,8 +6650,8 @@ impl<'db> TypeInferenceBuilder<'db> { value_ty, generic_context.signature(self.db()), )); - let bindings = match Bindings::match_parameters(signatures, &mut call_argument_types) - .check_types(self.db(), &mut call_argument_types) + let bindings = match Bindings::match_parameters(signatures, &call_argument_types) + .check_types(self.db(), &call_argument_types) { Ok(bindings) => bindings, Err(CallError(_, bindings)) => { @@ -6893,7 +6893,7 @@ impl<'db> TypeInferenceBuilder<'db> { match ty.try_call( self.db(), - &mut CallArgumentTypes::positional([value_ty, slice_ty]), + &CallArgumentTypes::positional([value_ty, slice_ty]), ) { Ok(bindings) => return bindings.return_type(self.db()), Err(CallError(_, bindings)) => {