1
Fork 0

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.
This commit is contained in:
Matthias Krüger 2024-11-04 18:12:48 +01:00 committed by GitHub
commit a4f323ce9c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 155 additions and 0 deletions

View file

@ -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);
}
}

View file

@ -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<'_>,