diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2024-11-04 18:12:48 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-11-04 18:12:48 +0100 |
| commit | a4f323ce9c00a1a51eae998c373103e23b622a7a (patch) | |
| tree | 0788c16493dc1a71ea5277855be55abb0eaf06af /compiler/rustc_trait_selection/src | |
| parent | 46ae1555e26d0bc39651cdb249b2ae740d46f3a3 (diff) | |
| parent | 5a48fe2c202a7aff935daf6b7cc56a12ad0ae72a (diff) | |
| download | rust-a4f323ce9c00a1a51eae998c373103e23b622a7a.tar.gz rust-a4f323ce9c00a1a51eae998c373103e23b622a7a.zip | |
Rollup merge of #132583 - mejrs:tuples, r=compiler-errors
Suggest creating unary tuples when types don't match a trait
When you want to have a variadic function, a common workaround to implement this is to create a trait and then implement that trait for various tuples. For example in `pyo3` there exists
```rust
/// Calls the object with only positional arguments.
pub fn call1(&self, args: impl IntoPy<Py<PyTuple>>) -> PyResult<&PyAny> {
...
}
```
with various impls like
```rust
impl<A: IntoPy<PyObject> IntoPy<Py<PyAny>> for (A,)
impl<A: IntoPy<PyObject, B: IntoPy<PyObject> IntoPy<Py<PyAny>> for (A, B)
... etc
```
This means that if you want to call the method with a single item you have to create a unary tuple, like `(x,)`, rather than just `x`.
This PR implements a suggestion to do that, if applicable.
Diffstat (limited to 'compiler/rustc_trait_selection/src')
| -rw-r--r-- | compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs | 35 |
2 files changed, 39 insertions, 0 deletions
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index f5cd99222e3..a1c97a38567 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -471,6 +471,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } self.try_to_add_help_message( + &root_obligation, &obligation, leaf_trait_predicate, &mut err, @@ -2488,6 +2489,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn try_to_add_help_message( &self, + root_obligation: &PredicateObligation<'tcx>, obligation: &PredicateObligation<'tcx>, trait_predicate: ty::PolyTraitPredicate<'tcx>, err: &mut Diag<'_>, @@ -2575,6 +2577,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { impl_candidates.as_slice(), span, ); + + self.suggest_tuple_wrapping(err, root_obligation, obligation); } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index e155effa1e3..86fd4c230f6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4452,6 +4452,41 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } + /// If the type failed selection but the trait is implemented for `(T,)`, suggest that the user + /// creates a unary tuple + /// + /// This is a common gotcha when using libraries that emulate variadic functions with traits for tuples. + pub(super) fn suggest_tuple_wrapping( + &self, + err: &mut Diag<'_>, + root_obligation: &PredicateObligation<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) { + let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code() else { + return; + }; + + let Some(root_pred) = root_obligation.predicate.as_trait_clause() else { return }; + + let trait_ref = root_pred.map_bound(|root_pred| { + root_pred + .trait_ref + .with_self_ty(self.tcx, Ty::new_tup(self.tcx, &[root_pred.trait_ref.self_ty()])) + }); + + let obligation = + Obligation::new(self.tcx, obligation.cause.clone(), obligation.param_env, trait_ref); + + if self.predicate_must_hold_modulo_regions(&obligation) { + let arg_span = self.tcx.hir().span(*arg_hir_id); + err.multipart_suggestion_verbose( + format!("use a unary tuple instead"), + vec![(arg_span.shrink_to_lo(), "(".into()), (arg_span.shrink_to_hi(), ",)".into())], + Applicability::MaybeIncorrect, + ); + } + } + pub(super) fn explain_hrtb_projection( &self, diag: &mut Diag<'_>, |
