Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest creating unary tuples when types don't match a trait #132583

Merged
merged 1 commit into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}

self.try_to_add_help_message(
&root_obligation,
&obligation,
leaf_trait_predicate,
&mut err,
Expand Down Expand Up @@ -2428,6 +2429,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<'_>,
Expand Down Expand Up @@ -2511,6 +2513,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
impl_candidates.as_slice(),
span,
);

self.suggest_tuple_wrapping(err, root_obligation, obligation);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4436,6 +4436,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<'_>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ note: required by a bound in `check`
|
LL | fn check(a: impl Foo) {}
| ^^^ required by this bound in `check`
help: use a unary tuple instead
|
LL | check(((),));
| + ++

error: aborting due to 1 previous error

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ note: required by a bound in `check`
|
LL | fn check(a: impl Foo) {}
| ^^^ required by this bound in `check`
help: use a unary tuple instead
|
LL | check(((),));
| + ++

error: aborting due to 1 previous error

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ note: required by a bound in `check`
|
LL | fn check(a: impl Foo) {}
| ^^^ required by this bound in `check`
help: use a unary tuple instead
|
LL | check(((),));
| + ++
Comment on lines +17 to +18
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this suggestion okay? I'm not quite sure what #[do_not_recommend] is supposed to (not) show. Its rfc only talks about not mentioning confusing traits.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine.


error: aborting due to 1 previous error

Expand Down
4 changes: 4 additions & 0 deletions tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ LL | cachedcoso.call_once(1);
|
note: required by a bound in `call_once`
--> $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

Expand Down
19 changes: 19 additions & 0 deletions tests/ui/on-unimplemented/suggest_tuple_wrap.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 25 additions & 0 deletions tests/ui/on-unimplemented/suggest_tuple_wrap.stderr
Original file line number Diff line number Diff line change
@@ -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`.
26 changes: 26 additions & 0 deletions tests/ui/on-unimplemented/suggest_tuple_wrap_root_obligation.rs
Original file line number Diff line number Diff line change
@@ -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>`
}
Original file line number Diff line number Diff line change
@@ -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`.
4 changes: 4 additions & 0 deletions tests/ui/overloaded/overloaded-calls-nontuple.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ LL | self.call_mut(z)
|
note: required by a bound in `call_mut`
--> $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
--> $DIR/overloaded-calls-nontuple.rs:29:10
Expand Down
Loading