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:
commit
a4f323ce9c
@ -471,6 +471,7 @@ pub fn report_selection_error(
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.try_to_add_help_message(
|
self.try_to_add_help_message(
|
||||||
|
&root_obligation,
|
||||||
&obligation,
|
&obligation,
|
||||||
leaf_trait_predicate,
|
leaf_trait_predicate,
|
||||||
&mut err,
|
&mut err,
|
||||||
@ -2488,6 +2489,7 @@ fn add_tuple_trait_message(
|
|||||||
|
|
||||||
fn try_to_add_help_message(
|
fn try_to_add_help_message(
|
||||||
&self,
|
&self,
|
||||||
|
root_obligation: &PredicateObligation<'tcx>,
|
||||||
obligation: &PredicateObligation<'tcx>,
|
obligation: &PredicateObligation<'tcx>,
|
||||||
trait_predicate: ty::PolyTraitPredicate<'tcx>,
|
trait_predicate: ty::PolyTraitPredicate<'tcx>,
|
||||||
err: &mut Diag<'_>,
|
err: &mut Diag<'_>,
|
||||||
@ -2575,6 +2577,8 @@ fn try_to_add_help_message(
|
|||||||
impl_candidates.as_slice(),
|
impl_candidates.as_slice(),
|
||||||
span,
|
span,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self.suggest_tuple_wrapping(err, root_obligation, obligation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4452,6 +4452,41 @@ pub(super) fn suggest_convert_to_slice(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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(
|
pub(super) fn explain_hrtb_projection(
|
||||||
&self,
|
&self,
|
||||||
diag: &mut Diag<'_>,
|
diag: &mut Diag<'_>,
|
||||||
|
@ -15,6 +15,10 @@ note: required by a bound in `check`
|
|||||||
|
|
|
|
||||||
LL | fn check(a: impl Foo) {}
|
LL | fn check(a: impl Foo) {}
|
||||||
| ^^^ required by this bound in `check`
|
| ^^^ required by this bound in `check`
|
||||||
|
help: use a unary tuple instead
|
||||||
|
|
|
||||||
|
LL | check(((),));
|
||||||
|
| + ++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -12,6 +12,10 @@ note: required by a bound in `check`
|
|||||||
|
|
|
|
||||||
LL | fn check(a: impl Foo) {}
|
LL | fn check(a: impl Foo) {}
|
||||||
| ^^^ required by this bound in `check`
|
| ^^^ required by this bound in `check`
|
||||||
|
help: use a unary tuple instead
|
||||||
|
|
|
||||||
|
LL | check(((),));
|
||||||
|
| + ++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -12,6 +12,10 @@ note: required by a bound in `check`
|
|||||||
|
|
|
|
||||||
LL | fn check(a: impl Foo) {}
|
LL | fn check(a: impl Foo) {}
|
||||||
| ^^^ required by this bound in `check`
|
| ^^^ required by this bound in `check`
|
||||||
|
help: use a unary tuple instead
|
||||||
|
|
|
||||||
|
LL | check(((),));
|
||||||
|
| + ++
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
@ -71,6 +71,10 @@ LL | cachedcoso.call_once(1);
|
|||||||
|
|
|
|
||||||
note: required by a bound in `call_once`
|
note: required by a bound in `call_once`
|
||||||
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
|
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
|
||||||
|
help: use a unary tuple instead
|
||||||
|
|
|
||||||
|
LL | cachedcoso.call_once((1,));
|
||||||
|
| + ++
|
||||||
|
|
||||||
error: aborting due to 6 previous errors
|
error: aborting due to 6 previous errors
|
||||||
|
|
||||||
|
19
tests/ui/on-unimplemented/suggest_tuple_wrap.rs
Normal file
19
tests/ui/on-unimplemented/suggest_tuple_wrap.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
pub trait Argument {}
|
||||||
|
impl Argument for u8 {}
|
||||||
|
impl Argument for i8 {}
|
||||||
|
impl Argument for String {}
|
||||||
|
impl Argument for &str {}
|
||||||
|
|
||||||
|
pub trait TupleArgs {}
|
||||||
|
impl<A: Argument> TupleArgs for (A,) {}
|
||||||
|
impl<A: Argument, B: Argument> TupleArgs for (A, B) {}
|
||||||
|
impl<A: Argument, B: Argument, C: Argument> TupleArgs for (A, B, C) {}
|
||||||
|
|
||||||
|
fn convert_into_tuple(_x: impl TupleArgs) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
convert_into_tuple(42_u8);
|
||||||
|
//~^ ERROR E0277
|
||||||
|
//~| HELP the following other types implement trait `TupleArgs`
|
||||||
|
//~| HELP use a unary tuple instead
|
||||||
|
}
|
25
tests/ui/on-unimplemented/suggest_tuple_wrap.stderr
Normal file
25
tests/ui/on-unimplemented/suggest_tuple_wrap.stderr
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
error[E0277]: the trait bound `u8: TupleArgs` is not satisfied
|
||||||
|
--> $DIR/suggest_tuple_wrap.rs:15:24
|
||||||
|
|
|
||||||
|
LL | convert_into_tuple(42_u8);
|
||||||
|
| ------------------ ^^^^^ the trait `TupleArgs` is not implemented for `u8`
|
||||||
|
| |
|
||||||
|
| required by a bound introduced by this call
|
||||||
|
|
|
||||||
|
= help: the following other types implement trait `TupleArgs`:
|
||||||
|
(A, B)
|
||||||
|
(A, B, C)
|
||||||
|
(A,)
|
||||||
|
note: required by a bound in `convert_into_tuple`
|
||||||
|
--> $DIR/suggest_tuple_wrap.rs:12:32
|
||||||
|
|
|
||||||
|
LL | fn convert_into_tuple(_x: impl TupleArgs) {}
|
||||||
|
| ^^^^^^^^^ required by this bound in `convert_into_tuple`
|
||||||
|
help: use a unary tuple instead
|
||||||
|
|
|
||||||
|
LL | convert_into_tuple((42_u8,));
|
||||||
|
| + ++
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
@ -0,0 +1,26 @@
|
|||||||
|
struct Tuple;
|
||||||
|
|
||||||
|
impl From<(u8,)> for Tuple {
|
||||||
|
fn from(_: (u8,)) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<(u8, u8)> for Tuple {
|
||||||
|
fn from(_: (u8, u8)) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<(u8, u8, u8)> for Tuple {
|
||||||
|
fn from(_: (u8, u8, u8)) -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_into_tuple(_x: impl Into<Tuple>) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
convert_into_tuple(42_u8);
|
||||||
|
//~^ ERROR E0277
|
||||||
|
//~| HELP use a unary tuple instead
|
||||||
|
//~| HELP the following other types implement trait `From<T>`
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
error[E0277]: the trait bound `Tuple: From<u8>` is not satisfied
|
||||||
|
--> $DIR/suggest_tuple_wrap_root_obligation.rs:22:24
|
||||||
|
|
|
||||||
|
LL | convert_into_tuple(42_u8);
|
||||||
|
| ------------------ ^^^^^ the trait `From<u8>` is not implemented for `Tuple`
|
||||||
|
| |
|
||||||
|
| required by a bound introduced by this call
|
||||||
|
|
|
||||||
|
= help: the following other types implement trait `From<T>`:
|
||||||
|
`Tuple` implements `From<(u8, u8)>`
|
||||||
|
`Tuple` implements `From<(u8, u8, u8)>`
|
||||||
|
`Tuple` implements `From<(u8,)>`
|
||||||
|
= note: required for `u8` to implement `Into<Tuple>`
|
||||||
|
note: required by a bound in `convert_into_tuple`
|
||||||
|
--> $DIR/suggest_tuple_wrap_root_obligation.rs:19:32
|
||||||
|
|
|
||||||
|
LL | fn convert_into_tuple(_x: impl Into<Tuple>) {}
|
||||||
|
| ^^^^^^^^^^^ required by this bound in `convert_into_tuple`
|
||||||
|
help: use a unary tuple instead
|
||||||
|
|
|
||||||
|
LL | convert_into_tuple((42_u8,));
|
||||||
|
| + ++
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
@ -38,6 +38,10 @@ LL | self.call_mut(z)
|
|||||||
|
|
|
|
||||||
note: required by a bound in `call_mut`
|
note: required by a bound in `call_mut`
|
||||||
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
|
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
|
||||||
|
help: use a unary tuple instead
|
||||||
|
|
|
||||||
|
LL | self.call_mut((z,))
|
||||||
|
| + ++
|
||||||
|
|
||||||
error[E0059]: cannot use call notation; the first type parameter for the function trait is neither a tuple nor unit
|
error[E0059]: cannot use call notation; the first type parameter for the function trait is neither a tuple nor unit
|
||||||
--> $DIR/overloaded-calls-nontuple.rs:29:10
|
--> $DIR/overloaded-calls-nontuple.rs:29:10
|
||||||
|
Loading…
Reference in New Issue
Block a user