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 49fa21e50c059..3920777a8502f 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 @@ -192,19 +192,38 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let have_alt_message = message.is_some() || label.is_some(); let is_try_conversion = self.is_try_conversion(span, main_trait_predicate.def_id()); + let is_question_mark = matches!( + root_obligation.cause.code().peel_derives(), + ObligationCauseCode::QuestionMark, + ) && !( + self.tcx.is_diagnostic_item(sym::FromResidual, main_trait_predicate.def_id()) + || self.tcx.is_lang_item(main_trait_predicate.def_id(), LangItem::Try) + ); let is_unsize = self.tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Unsize); + let question_mark_message = "the question mark operation (`?`) implicitly \ + performs a conversion on the error value \ + using the `From` trait"; let (message, notes, append_const_msg) = if is_try_conversion { + // We have a `-> Result<_, E1>` and `gives_E2()?`. ( Some(format!( "`?` couldn't convert the error to `{}`", main_trait_predicate.skip_binder().self_ty(), )), - vec![ - "the question mark operation (`?`) implicitly performs a \ - conversion on the error value using the `From` trait" - .to_owned(), - ], + vec![question_mark_message.to_owned()], + Some(AppendConstMessage::Default), + ) + } else if is_question_mark { + // Similar to the case above, but in this case the conversion is for a + // trait object: `-> Result<_, Box` and `gives_E()?` when + // `E: Error` isn't met. + ( + Some(format!( + "`?` couldn't convert the error: `{main_trait_predicate}` is \ + not satisfied", + )), + vec![question_mark_message.to_owned()], Some(AppendConstMessage::Default), ) } else { @@ -220,8 +239,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &mut long_ty_file, ); - let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(main_trait_predicate.def_id(), LangItem::TransmuteTrait) - { + let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item( + main_trait_predicate.def_id(), + LangItem::TransmuteTrait, + ) { // Recompute the safe transmute reason and use that for the error reporting match self.get_safe_transmute_error_and_reason( obligation.clone(), @@ -249,18 +270,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { *err.long_ty_path() = long_ty_file; let mut suggested = false; - if is_try_conversion { + if is_try_conversion || is_question_mark { suggested = self.try_conversion_context(&obligation, main_trait_predicate, &mut err); } - if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) { - err.span_label( - ret_span, - format!( - "expected `{}` because of this", - main_trait_predicate.skip_binder().self_ty() - ), - ); + if let Some(ret_span) = self.return_type_span(&obligation) { + if is_try_conversion { + err.span_label( + ret_span, + format!( + "expected `{}` because of this", + main_trait_predicate.skip_binder().self_ty() + ), + ); + } else if is_question_mark { + err.span_label(ret_span, format!("required `{main_trait_predicate}` because of this")); + } } if tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Tuple) { @@ -302,10 +327,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // If it has a custom `#[rustc_on_unimplemented]` // error message, let's display it as the label! err.span_label(span, s); - if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) { + if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point // at the type param with a label to suggest constraining it. + && !self.tcx.is_diagnostic_item(sym::FromResidual, leaf_trait_predicate.def_id()) + // Don't say "the trait `FromResidual>` is + // not implemented for `Result`". + { err.help(explanation); } } else if let Some(custom_explanation) = safe_transmute_explanation { @@ -2035,6 +2064,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return false; } if let &[cand] = &candidates[..] { + if self.tcx.is_diagnostic_item(sym::FromResidual, cand.def_id) + && !self.tcx.features().enabled(sym::try_trait_v2) + { + return false; + } let (desc, mention_castable) = match (cand.self_ty().kind(), trait_pred.self_ty().skip_binder().kind()) { (ty::FnPtr(..), ty::FnDef(..)) => { diff --git a/tests/ui/async-await/issue-84841.stderr b/tests/ui/async-await/issue-84841.stderr index 69c1c882d60ac..0d008477310a7 100644 --- a/tests/ui/async-await/issue-84841.stderr +++ b/tests/ui/async-await/issue-84841.stderr @@ -17,8 +17,6 @@ LL | | test()?; ... | LL | | } | |_- this function should return `Result` or `Option` to accept `?` - | - = help: the trait `FromResidual<_>` is not implemented for `()` error: aborting due to 2 previous errors diff --git a/tests/ui/async-await/try-on-option-in-async.stderr b/tests/ui/async-await/try-on-option-in-async.stderr index 9e0bb42a697cc..332f4d4ec0c27 100644 --- a/tests/ui/async-await/try-on-option-in-async.stderr +++ b/tests/ui/async-await/try-on-option-in-async.stderr @@ -6,8 +6,6 @@ LL | async { LL | let x: Option = None; LL | x?; | ^ cannot use the `?` operator in an async block that returns `{integer}` - | - = help: the trait `FromResidual>` is not implemented for `{integer}` error[E0277]: the `?` operator can only be used in an async closure that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-on-option-in-async.rs:16:10 @@ -20,8 +18,6 @@ LL | | x?; LL | | 22_u32 LL | | }; | |_____- this function should return `Result` or `Option` to accept `?` - | - = help: the trait `FromResidual>` is not implemented for `u32` error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-on-option-in-async.rs:25:6 @@ -34,8 +30,6 @@ LL | | x?; LL | | 22 LL | | } | |_- this function should return `Result` or `Option` to accept `?` - | - = help: the trait `FromResidual>` is not implemented for `u32` error: aborting due to 3 previous errors diff --git a/tests/ui/return/return-from-residual-sugg-issue-125997.stderr b/tests/ui/return/return-from-residual-sugg-issue-125997.stderr index e22f33fd242a6..877995dfe1266 100644 --- a/tests/ui/return/return-from-residual-sugg-issue-125997.stderr +++ b/tests/ui/return/return-from-residual-sugg-issue-125997.stderr @@ -6,7 +6,6 @@ LL | fn test1() { LL | let mut _file = File::create("foo.txt")?; | ^ cannot use the `?` operator in a function that returns `()` | - = help: the trait `FromResidual>` is not implemented for `()` help: consider adding return type | LL ~ fn test1() -> Result<(), Box> { @@ -23,7 +22,6 @@ LL | fn test2() { LL | let mut _file = File::create("foo.txt")?; | ^ cannot use the `?` operator in a function that returns `()` | - = help: the trait `FromResidual>` is not implemented for `()` help: consider adding return type | LL ~ fn test2() -> Result<(), Box> { @@ -41,7 +39,6 @@ LL | fn test4(&self) { LL | let mut _file = File::create("foo.txt")?; | ^ cannot use the `?` operator in a method that returns `()` | - = help: the trait `FromResidual>` is not implemented for `()` help: consider adding return type | LL ~ fn test4(&self) -> Result<(), Box> { @@ -59,7 +56,6 @@ LL | fn test5(&self) { LL | let mut _file = File::create("foo.txt")?; | ^ cannot use the `?` operator in a method that returns `()` | - = help: the trait `FromResidual>` is not implemented for `()` help: consider adding return type | LL ~ fn test5(&self) -> Result<(), Box> { @@ -78,7 +74,6 @@ LL | fn main() { LL | let mut _file = File::create("foo.txt")?; | ^ cannot use the `?` operator in a function that returns `()` | - = help: the trait `FromResidual>` is not implemented for `()` help: consider adding return type | LL ~ fn main() -> Result<(), Box> { @@ -99,7 +94,6 @@ LL | let mut _file = File::create("foo.txt")?; LL | mac!(); | ------ in this macro invocation | - = help: the trait `FromResidual>` is not implemented for `()` = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider adding return type | diff --git a/tests/ui/try-trait/bad-interconversion.stderr b/tests/ui/try-trait/bad-interconversion.stderr index 45422be946e82..6df05747f32c4 100644 --- a/tests/ui/try-trait/bad-interconversion.stderr +++ b/tests/ui/try-trait/bad-interconversion.stderr @@ -20,9 +20,6 @@ LL | fn option_to_result() -> Result { | -------------------------------------------- this function returns a `Result` LL | Some(3)?; | ^ use `.ok_or(...)?` to provide an error compatible with `Result` - | - = help: the trait `FromResidual>` is not implemented for `Result` - = help: the trait `FromResidual>` is implemented for `Result` error[E0277]: the `?` operator can only be used on `Result`s in a function that returns `Result` --> $DIR/bad-interconversion.rs:15:31 @@ -31,9 +28,6 @@ LL | fn control_flow_to_result() -> Result { | -------------------------------------------------- this function returns a `Result` LL | Ok(ControlFlow::Break(123)?) | ^ this `?` produces `ControlFlow<{integer}, Infallible>`, which is incompatible with `Result` - | - = help: the trait `FromResidual>` is not implemented for `Result` - = help: the trait `FromResidual>` is implemented for `Result` error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option` --> $DIR/bad-interconversion.rs:20:22 @@ -42,9 +36,6 @@ LL | fn result_to_option() -> Option { | ------------------------------------ this function returns an `Option` LL | Some(Err("hello")?) | ^ use `.ok()?` if you want to discard the `Result` error information - | - = help: the trait `FromResidual>` is not implemented for `Option` - = help: the trait `FromResidual>` is implemented for `Option` error[E0277]: the `?` operator can only be used on `Option`s in a function that returns `Option` --> $DIR/bad-interconversion.rs:25:33 @@ -53,9 +44,6 @@ LL | fn control_flow_to_option() -> Option { | ------------------------------------------ this function returns an `Option` LL | Some(ControlFlow::Break(123)?) | ^ this `?` produces `ControlFlow<{integer}, Infallible>`, which is incompatible with `Option` - | - = help: the trait `FromResidual>` is not implemented for `Option` - = help: the trait `FromResidual>` is implemented for `Option` error[E0277]: the `?` operator can only be used on `ControlFlow`s in a function that returns `ControlFlow` --> $DIR/bad-interconversion.rs:30:39 diff --git a/tests/ui/try-trait/bad-question-mark-on-trait-object.rs b/tests/ui/try-trait/bad-question-mark-on-trait-object.rs new file mode 100644 index 0000000000000..f319610cb3aec --- /dev/null +++ b/tests/ui/try-trait/bad-question-mark-on-trait-object.rs @@ -0,0 +1,26 @@ +struct E; +struct X; + +fn foo() -> Result<(), Box> { //~ NOTE required `E: std::error::Error` because of this + Ok(bar()?) + //~^ ERROR `?` couldn't convert the error: `E: std::error::Error` is not satisfied + //~| NOTE the trait `std::error::Error` is not implemented for `E` + //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + //~| NOTE required for `Box` to implement `From` + //~| NOTE in this expansion + //~| NOTE in this expansion + //~| NOTE in this expansion +} +fn bat() -> Result<(), X> { //~ NOTE expected `X` because of this + Ok(bar()?) + //~^ ERROR `?` couldn't convert the error to `X` + //~| NOTE the trait `From` is not implemented for `X` + //~| NOTE this can't be annotated with `?` because it has type `Result<_, E>` + //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + //~| NOTE in this expansion + //~| NOTE in this expansion +} +fn bar() -> Result<(), E> { + Err(E) +} +fn main() {} diff --git a/tests/ui/try-trait/bad-question-mark-on-trait-object.stderr b/tests/ui/try-trait/bad-question-mark-on-trait-object.stderr new file mode 100644 index 0000000000000..4e7cd1e288963 --- /dev/null +++ b/tests/ui/try-trait/bad-question-mark-on-trait-object.stderr @@ -0,0 +1,26 @@ +error[E0277]: `?` couldn't convert the error: `E: std::error::Error` is not satisfied + --> $DIR/bad-question-mark-on-trait-object.rs:5:13 + | +LL | fn foo() -> Result<(), Box> { + | -------------------------------------- required `E: std::error::Error` because of this +LL | Ok(bar()?) + | ^ the trait `std::error::Error` is not implemented for `E` + | + = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + = note: required for `Box` to implement `From` + +error[E0277]: `?` couldn't convert the error to `X` + --> $DIR/bad-question-mark-on-trait-object.rs:15:13 + | +LL | fn bat() -> Result<(), X> { + | ------------- expected `X` because of this +LL | Ok(bar()?) + | -----^ the trait `From` is not implemented for `X` + | | + | this can't be annotated with `?` because it has type `Result<_, E>` + | + = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/try-trait/option-to-result.stderr b/tests/ui/try-trait/option-to-result.stderr index 1a5a925f92fce..8a4c4707942ce 100644 --- a/tests/ui/try-trait/option-to-result.stderr +++ b/tests/ui/try-trait/option-to-result.stderr @@ -6,9 +6,6 @@ LL | fn test_result() -> Result<(),()> { LL | let a:Option<()> = Some(()); LL | a?; | ^ use `.ok_or(...)?` to provide an error compatible with `Result<(), ()>` - | - = help: the trait `FromResidual>` is not implemented for `Result<(), ()>` - = help: the trait `FromResidual>` is implemented for `Result` error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option` --> $DIR/option-to-result.rs:11:6 @@ -18,9 +15,6 @@ LL | fn test_option() -> Option{ LL | let a:Result = Ok(5); LL | a?; | ^ use `.ok()?` if you want to discard the `Result` error information - | - = help: the trait `FromResidual>` is not implemented for `Option` - = help: the trait `FromResidual>` is implemented for `Option` error: aborting due to 2 previous errors diff --git a/tests/ui/try-trait/try-on-option-diagnostics.stderr b/tests/ui/try-trait/try-on-option-diagnostics.stderr index 9ee540c79fdda..08675e242a354 100644 --- a/tests/ui/try-trait/try-on-option-diagnostics.stderr +++ b/tests/ui/try-trait/try-on-option-diagnostics.stderr @@ -6,8 +6,6 @@ LL | fn a_function() -> u32 { LL | let x: Option = None; LL | x?; | ^ cannot use the `?` operator in a function that returns `u32` - | - = help: the trait `FromResidual>` is not implemented for `u32` error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-on-option-diagnostics.rs:14:10 @@ -17,8 +15,6 @@ LL | let a_closure = || { LL | let x: Option = None; LL | x?; | ^ cannot use the `?` operator in a closure that returns `{integer}` - | - = help: the trait `FromResidual>` is not implemented for `{integer}` error[E0277]: the `?` operator can only be used in a method that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-on-option-diagnostics.rs:26:14 @@ -28,8 +24,6 @@ LL | fn a_method() { LL | let x: Option = None; LL | x?; | ^ cannot use the `?` operator in a method that returns `()` - | - = help: the trait `FromResidual>` is not implemented for `()` error[E0277]: the `?` operator can only be used in a trait method that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-on-option-diagnostics.rs:39:14 @@ -39,8 +33,6 @@ LL | fn a_trait_method() { LL | let x: Option = None; LL | x?; | ^ cannot use the `?` operator in a trait method that returns `()` - | - = help: the trait `FromResidual>` is not implemented for `()` error: aborting due to 4 previous errors diff --git a/tests/ui/try-trait/try-on-option.stderr b/tests/ui/try-trait/try-on-option.stderr index 15d0b28ddc1d7..aeb519086d8c1 100644 --- a/tests/ui/try-trait/try-on-option.stderr +++ b/tests/ui/try-trait/try-on-option.stderr @@ -6,9 +6,6 @@ LL | fn foo() -> Result { LL | let x: Option = None; LL | x?; | ^ use `.ok_or(...)?` to provide an error compatible with `Result` - | - = help: the trait `FromResidual>` is not implemented for `Result` - = help: the trait `FromResidual>` is implemented for `Result` error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) --> $DIR/try-on-option.rs:11:6 @@ -18,8 +15,6 @@ LL | fn bar() -> u32 { LL | let x: Option = None; LL | x?; | ^ cannot use the `?` operator in a function that returns `u32` - | - = help: the trait `FromResidual>` is not implemented for `u32` error: aborting due to 2 previous errors diff --git a/tests/ui/try-trait/try-operator-on-main.stderr b/tests/ui/try-trait/try-operator-on-main.stderr index 311e8076ed48e..9c2526442ab5b 100644 --- a/tests/ui/try-trait/try-operator-on-main.stderr +++ b/tests/ui/try-trait/try-operator-on-main.stderr @@ -7,7 +7,6 @@ LL | // error for a `Try` type on a non-`Try` fn LL | std::fs::File::open("foo")?; | ^ cannot use the `?` operator in a function that returns `()` | - = help: the trait `FromResidual>` is not implemented for `()` help: consider adding return type | LL ~ fn main() -> Result<(), Box> { @@ -33,8 +32,6 @@ LL | fn main() { ... LL | ()?; | ^ cannot use the `?` operator in a function that returns `()` - | - = help: the trait `FromResidual<_>` is not implemented for `()` error[E0277]: the trait bound `(): Try` is not satisfied --> $DIR/try-operator-on-main.rs:14:25