From d94b2d3578ef1d43d113db3dbd0ee46402476f2b Mon Sep 17 00:00:00 2001 From: Douglas Creager Date: Mon, 15 Dec 2025 14:38:54 -0500 Subject: [PATCH] error message --- .../ty_python_semantic/src/types/call/bind.rs | 51 +++++++++++++++---- .../ty_python_semantic/src/types/generics.rs | 30 ++++++++--- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index 005013e70b..86ba312ba8 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -4445,7 +4445,6 @@ impl<'db> BindingError<'db> { return; }; - let typevar = error.bound_typevar().typevar(context.db()); let argument_type = error.argument_type(); let argument_ty_display = argument_type.display(context.db()); @@ -4458,21 +4457,51 @@ impl<'db> BindingError<'db> { } )); - let typevar_name = typevar.name(context.db()); match error { - SpecializationError::MismatchedBound { .. } => { - diag.set_primary_message(format_args!("Argument type `{argument_ty_display}` does not satisfy upper bound `{}` of type variable `{typevar_name}`", - typevar.upper_bound(context.db()).expect("type variable should have an upper bound if this error occurs").display(context.db()) - )); + SpecializationError::NoSolution { parameter, .. } => { + diag.set_primary_message(format_args!( + "Argument type `{argument_ty_display}` does not \ + satisfy generic parameter annotation `{}", + parameter.display(context.db()), + )); } - SpecializationError::MismatchedConstraint { .. } => { - diag.set_primary_message(format_args!("Argument type `{argument_ty_display}` does not satisfy constraints ({}) of type variable `{typevar_name}`", - typevar.constraints(context.db()).expect("type variable should have constraints if this error occurs").iter().map(|ty| format!("`{}`", ty.display(context.db()))).join(", ") - )); + SpecializationError::MismatchedBound { bound_typevar, .. } => { + let typevar = bound_typevar.typevar(context.db()); + let typevar_name = typevar.name(context.db()); + diag.set_primary_message(format_args!( + "Argument type `{argument_ty_display}` does not \ + satisfy upper bound `{}` of type variable `{typevar_name}`", + typevar + .upper_bound(context.db()) + .expect( + "type variable should have an upper bound if this error occurs" + ) + .display(context.db()) + )); + } + SpecializationError::MismatchedConstraint { bound_typevar, .. } => { + let typevar = bound_typevar.typevar(context.db()); + let typevar_name = typevar.name(context.db()); + diag.set_primary_message(format_args!( + "Argument type `{argument_ty_display}` does not \ + satisfy constraints ({}) of type variable `{typevar_name}`", + typevar + .constraints(context.db()) + .expect( + "type variable should have constraints if this error occurs" + ) + .iter() + .format_with(", ", |ty, f| f(&format_args!( + "`{}`", + ty.display(context.db()) + ))) + )); } } - if let Some(typevar_definition) = typevar.definition(context.db()) { + if let Some(typevar_definition) = error.bound_typevar().and_then(|bound_typevar| { + bound_typevar.typevar(context.db()).definition(context.db()) + }) { let module = parsed_module(context.db(), typevar_definition.file(context.db())) .load(context.db()); let typevar_range = typevar_definition.full_range(context.db(), &module); diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index 687b0dbad9..fe3014e47f 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -1587,7 +1587,6 @@ impl<'db> SpecializationBuilder<'db> { upper: Vec>, } - let constraints = constraints.limit_to_valid_specializations(self.db); let mut sorted_paths = Vec::new(); constraints.for_each_path(self.db, |path| { let mut path: Vec<_> = path.positive_constraints().collect(); @@ -1884,8 +1883,15 @@ impl<'db> SpecializationBuilder<'db> { return Ok(()); } - let when = - actual.when_constraint_set_assignable_to(self.db, formal, self.inferable); + let when = actual + .when_constraint_set_assignable_to(self.db, formal, self.inferable) + .limit_to_valid_specializations(self.db); + if when.is_never_satisfied(self.db) { + return Err(SpecializationError::NoSolution { + parameter: formal, + argument: actual, + }); + } self.add_type_mappings_from_constraint_set(formal, when, &mut f); } @@ -1905,7 +1911,8 @@ impl<'db> SpecializationBuilder<'db> { self.db, formal_callable, self.inferable, - ); + ) + .limit_to_valid_specializations(self.db); self.add_type_mappings_from_constraint_set(formal, when, &mut f); } else { for actual_signature in &actual_callable.signatures(self.db).overloads { @@ -1914,7 +1921,8 @@ impl<'db> SpecializationBuilder<'db> { self.db, formal_callable, self.inferable, - ); + ) + .limit_to_valid_specializations(self.db); self.add_type_mappings_from_constraint_set(formal, when, &mut f); } } @@ -1931,6 +1939,10 @@ impl<'db> SpecializationBuilder<'db> { #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) enum SpecializationError<'db> { + NoSolution { + parameter: Type<'db>, + argument: Type<'db>, + }, MismatchedBound { bound_typevar: BoundTypeVarInstance<'db>, argument: Type<'db>, @@ -1942,15 +1954,17 @@ pub(crate) enum SpecializationError<'db> { } impl<'db> SpecializationError<'db> { - pub(crate) fn bound_typevar(&self) -> BoundTypeVarInstance<'db> { + pub(crate) fn bound_typevar(&self) -> Option> { match self { - Self::MismatchedBound { bound_typevar, .. } => *bound_typevar, - Self::MismatchedConstraint { bound_typevar, .. } => *bound_typevar, + Self::NoSolution { .. } => None, + Self::MismatchedBound { bound_typevar, .. } => Some(*bound_typevar), + Self::MismatchedConstraint { bound_typevar, .. } => Some(*bound_typevar), } } pub(crate) fn argument_type(&self) -> Type<'db> { match self { + Self::NoSolution { argument, .. } => *argument, Self::MismatchedBound { argument, .. } => *argument, Self::MismatchedConstraint { argument, .. } => *argument, }