Skip to content

Commit a4f323c

Browse files
authored
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.
2 parents 46ae155 + 5a48fe2 commit a4f323c

11 files changed

+155
-0
lines changed

compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs

+4
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
471471
}
472472

473473
self.try_to_add_help_message(
474+
&root_obligation,
474475
&obligation,
475476
leaf_trait_predicate,
476477
&mut err,
@@ -2488,6 +2489,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
24882489

24892490
fn try_to_add_help_message(
24902491
&self,
2492+
root_obligation: &PredicateObligation<'tcx>,
24912493
obligation: &PredicateObligation<'tcx>,
24922494
trait_predicate: ty::PolyTraitPredicate<'tcx>,
24932495
err: &mut Diag<'_>,
@@ -2575,6 +2577,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
25752577
impl_candidates.as_slice(),
25762578
span,
25772579
);
2580+
2581+
self.suggest_tuple_wrapping(err, root_obligation, obligation);
25782582
}
25792583
}
25802584

compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs

+35
Original file line numberDiff line numberDiff line change
@@ -4452,6 +4452,41 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
44524452
}
44534453
}
44544454

4455+
/// If the type failed selection but the trait is implemented for `(T,)`, suggest that the user
4456+
/// creates a unary tuple
4457+
///
4458+
/// This is a common gotcha when using libraries that emulate variadic functions with traits for tuples.
4459+
pub(super) fn suggest_tuple_wrapping(
4460+
&self,
4461+
err: &mut Diag<'_>,
4462+
root_obligation: &PredicateObligation<'tcx>,
4463+
obligation: &PredicateObligation<'tcx>,
4464+
) {
4465+
let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code() else {
4466+
return;
4467+
};
4468+
4469+
let Some(root_pred) = root_obligation.predicate.as_trait_clause() else { return };
4470+
4471+
let trait_ref = root_pred.map_bound(|root_pred| {
4472+
root_pred
4473+
.trait_ref
4474+
.with_self_ty(self.tcx, Ty::new_tup(self.tcx, &[root_pred.trait_ref.self_ty()]))
4475+
});
4476+
4477+
let obligation =
4478+
Obligation::new(self.tcx, obligation.cause.clone(), obligation.param_env, trait_ref);
4479+
4480+
if self.predicate_must_hold_modulo_regions(&obligation) {
4481+
let arg_span = self.tcx.hir().span(*arg_hir_id);
4482+
err.multipart_suggestion_verbose(
4483+
format!("use a unary tuple instead"),
4484+
vec![(arg_span.shrink_to_lo(), "(".into()), (arg_span.shrink_to_hi(), ",)".into())],
4485+
Applicability::MaybeIncorrect,
4486+
);
4487+
}
4488+
}
4489+
44554490
pub(super) fn explain_hrtb_projection(
44564491
&self,
44574492
diag: &mut Diag<'_>,

tests/ui/diagnostic_namespace/do_not_recommend/do_not_apply_attribute_without_feature_flag.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ note: required by a bound in `check`
1515
|
1616
LL | fn check(a: impl Foo) {}
1717
| ^^^ required by this bound in `check`
18+
help: use a unary tuple instead
19+
|
20+
LL | check(((),));
21+
| + ++
1822

1923
error: aborting due to 1 previous error
2024

tests/ui/diagnostic_namespace/do_not_recommend/supress_suggestions_in_help.current.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ note: required by a bound in `check`
1212
|
1313
LL | fn check(a: impl Foo) {}
1414
| ^^^ required by this bound in `check`
15+
help: use a unary tuple instead
16+
|
17+
LL | check(((),));
18+
| + ++
1519

1620
error: aborting due to 1 previous error
1721

tests/ui/diagnostic_namespace/do_not_recommend/supress_suggestions_in_help.next.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ note: required by a bound in `check`
1212
|
1313
LL | fn check(a: impl Foo) {}
1414
| ^^^ required by this bound in `check`
15+
help: use a unary tuple instead
16+
|
17+
LL | check(((),));
18+
| + ++
1519

1620
error: aborting due to 1 previous error
1721

tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ LL | cachedcoso.call_once(1);
7171
|
7272
note: required by a bound in `call_once`
7373
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
74+
help: use a unary tuple instead
75+
|
76+
LL | cachedcoso.call_once((1,));
77+
| + ++
7478

7579
error: aborting due to 6 previous errors
7680

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
pub trait Argument {}
2+
impl Argument for u8 {}
3+
impl Argument for i8 {}
4+
impl Argument for String {}
5+
impl Argument for &str {}
6+
7+
pub trait TupleArgs {}
8+
impl<A: Argument> TupleArgs for (A,) {}
9+
impl<A: Argument, B: Argument> TupleArgs for (A, B) {}
10+
impl<A: Argument, B: Argument, C: Argument> TupleArgs for (A, B, C) {}
11+
12+
fn convert_into_tuple(_x: impl TupleArgs) {}
13+
14+
fn main() {
15+
convert_into_tuple(42_u8);
16+
//~^ ERROR E0277
17+
//~| HELP the following other types implement trait `TupleArgs`
18+
//~| HELP use a unary tuple instead
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0277]: the trait bound `u8: TupleArgs` is not satisfied
2+
--> $DIR/suggest_tuple_wrap.rs:15:24
3+
|
4+
LL | convert_into_tuple(42_u8);
5+
| ------------------ ^^^^^ the trait `TupleArgs` is not implemented for `u8`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the following other types implement trait `TupleArgs`:
10+
(A, B)
11+
(A, B, C)
12+
(A,)
13+
note: required by a bound in `convert_into_tuple`
14+
--> $DIR/suggest_tuple_wrap.rs:12:32
15+
|
16+
LL | fn convert_into_tuple(_x: impl TupleArgs) {}
17+
| ^^^^^^^^^ required by this bound in `convert_into_tuple`
18+
help: use a unary tuple instead
19+
|
20+
LL | convert_into_tuple((42_u8,));
21+
| + ++
22+
23+
error: aborting due to 1 previous error
24+
25+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
struct Tuple;
2+
3+
impl From<(u8,)> for Tuple {
4+
fn from(_: (u8,)) -> Self {
5+
todo!()
6+
}
7+
}
8+
impl From<(u8, u8)> for Tuple {
9+
fn from(_: (u8, u8)) -> Self {
10+
todo!()
11+
}
12+
}
13+
impl From<(u8, u8, u8)> for Tuple {
14+
fn from(_: (u8, u8, u8)) -> Self {
15+
todo!()
16+
}
17+
}
18+
19+
fn convert_into_tuple(_x: impl Into<Tuple>) {}
20+
21+
fn main() {
22+
convert_into_tuple(42_u8);
23+
//~^ ERROR E0277
24+
//~| HELP use a unary tuple instead
25+
//~| HELP the following other types implement trait `From<T>`
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error[E0277]: the trait bound `Tuple: From<u8>` is not satisfied
2+
--> $DIR/suggest_tuple_wrap_root_obligation.rs:22:24
3+
|
4+
LL | convert_into_tuple(42_u8);
5+
| ------------------ ^^^^^ the trait `From<u8>` is not implemented for `Tuple`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the following other types implement trait `From<T>`:
10+
`Tuple` implements `From<(u8, u8)>`
11+
`Tuple` implements `From<(u8, u8, u8)>`
12+
`Tuple` implements `From<(u8,)>`
13+
= note: required for `u8` to implement `Into<Tuple>`
14+
note: required by a bound in `convert_into_tuple`
15+
--> $DIR/suggest_tuple_wrap_root_obligation.rs:19:32
16+
|
17+
LL | fn convert_into_tuple(_x: impl Into<Tuple>) {}
18+
| ^^^^^^^^^^^ required by this bound in `convert_into_tuple`
19+
help: use a unary tuple instead
20+
|
21+
LL | convert_into_tuple((42_u8,));
22+
| + ++
23+
24+
error: aborting due to 1 previous error
25+
26+
For more information about this error, try `rustc --explain E0277`.

tests/ui/overloaded/overloaded-calls-nontuple.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ LL | self.call_mut(z)
3838
|
3939
note: required by a bound in `call_mut`
4040
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
41+
help: use a unary tuple instead
42+
|
43+
LL | self.call_mut((z,))
44+
| + ++
4145

4246
error[E0059]: cannot use call notation; the first type parameter for the function trait is neither a tuple nor unit
4347
--> $DIR/overloaded-calls-nontuple.rs:29:10

0 commit comments

Comments
 (0)