From 896ccb96063526818aa560af43d3a918df97fe48 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 5 Jun 2023 08:26:53 +0000 Subject: [PATCH 1/2] Properly compare types for `Option::as_deref` suggestion --- .../src/traits/error_reporting/suggestions.rs | 2 +- .../suggest-option-asderef-unfixable.rs | 6 ---- .../suggest-option-asderef-unfixable.stderr | 33 ++++--------------- .../suggest-option-asderef.fixed | 9 +++++ .../suggest-option-asderef.rs | 9 +++++ .../suggest-option-asderef.stderr | 33 ++++++++++++++++--- 6 files changed, 54 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 42038dbc3d82e..4ca83904f4754 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -3593,7 +3593,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { && let Some(deref_target_did) = tcx.lang_items().deref_target() && let projection = tcx.mk_projection(deref_target_did, tcx.mk_substs(&[ty::GenericArg::from(found_ty)])) && let Ok(deref_target) = tcx.try_normalize_erasing_regions(param_env, projection) - && deref_target == target_ty + && infcx.can_eq(param_env, deref_target, target_ty) { let help = if let hir::Mutability::Mut = needs_mut && let Some(deref_mut_did) = tcx.lang_items().deref_mut_trait() diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs index cc9ba5514fef1..ac0831ce65508 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.rs @@ -10,10 +10,6 @@ fn no_args() -> Option<()> { Some(()) } -fn generic_ref(_: &T) -> Option<()> { - Some(()) -} - extern "C" fn takes_str_but_wrong_abi(_: &str) -> Option<()> { Some(()) } @@ -33,8 +29,6 @@ fn main() { //~^ ERROR expected a `FnOnce<(String,)>` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` let _ = produces_string().and_then(no_args); //~^ ERROR function is expected to take 1 argument, but it takes 0 arguments - let _ = produces_string().and_then(generic_ref); - //~^ ERROR type mismatch in function arguments let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); //~^ ERROR type mismatch in function arguments } diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr index 079909eb48d1d..ecfbd27b180e6 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr @@ -1,5 +1,5 @@ error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef-unfixable.rs:28:40 + --> $DIR/suggest-option-asderef-unfixable.rs:24:40 | LL | fn takes_str_but_too_many_refs(_: &&str) -> Option<()> { | ------------------------------------------------------ found signature defined here @@ -15,7 +15,7 @@ note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL error[E0277]: expected a `FnOnce<(String,)>` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` - --> $DIR/suggest-option-asderef-unfixable.rs:30:40 + --> $DIR/suggest-option-asderef-unfixable.rs:26:40 | LL | let _ = produces_string().and_then(takes_str_but_wrong_abi); | -------- ^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnOnce<(String,)>` closure, found `for<'a> extern "C" fn(&'a str) -> Option<()> {takes_str_but_wrong_abi}` @@ -27,7 +27,7 @@ note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL error[E0277]: expected a `FnOnce<(String,)>` closure, found `for<'a> unsafe fn(&'a str) -> Option<()> {takes_str_but_unsafe}` - --> $DIR/suggest-option-asderef-unfixable.rs:32:40 + --> $DIR/suggest-option-asderef-unfixable.rs:28:40 | LL | let _ = produces_string().and_then(takes_str_but_unsafe); | -------- ^^^^^^^^^^^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }` @@ -40,7 +40,7 @@ note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL error[E0593]: function is expected to take 1 argument, but it takes 0 arguments - --> $DIR/suggest-option-asderef-unfixable.rs:34:40 + --> $DIR/suggest-option-asderef-unfixable.rs:30:40 | LL | fn no_args() -> Option<()> { | -------------------------- takes 0 arguments @@ -54,28 +54,7 @@ note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef-unfixable.rs:36:40 - | -LL | fn generic_ref(_: &T) -> Option<()> { - | -------------------------------------- found signature defined here -... -LL | let _ = produces_string().and_then(generic_ref); - | -------- ^^^^^^^^^^^ expected due to this - | | - | required by a bound introduced by this call - | - = note: expected function signature `fn(String) -> _` - found function signature `for<'a> fn(&'a _) -> _` -note: required by a bound in `Option::::and_then` - --> $SRC_DIR/core/src/option.rs:LL:COL -help: do not borrow the argument - | -LL - fn generic_ref(_: &T) -> Option<()> { -LL + fn generic_ref(_: T) -> Option<()> { - | - -error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef-unfixable.rs:38:45 + --> $DIR/suggest-option-asderef-unfixable.rs:32:45 | LL | fn takes_str_but_too_many_refs(_: &&str) -> Option<()> { | ------------------------------------------------------ found signature defined here @@ -90,7 +69,7 @@ LL | let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0277, E0593, E0631. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/mismatched_types/suggest-option-asderef.fixed b/tests/ui/mismatched_types/suggest-option-asderef.fixed index 08805999341ff..5c42ece3c5d09 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef.fixed +++ b/tests/ui/mismatched_types/suggest-option-asderef.fixed @@ -16,6 +16,11 @@ fn generic(_: T) -> Option<()> { Some(()) } +fn generic_ref(_: T) -> Option<()> { + //~^ HELP do not borrow the argument + Some(()) +} + fn main() { let _: Option<()> = produces_string().as_deref().and_then(takes_str); //~^ ERROR type mismatch in function arguments @@ -27,4 +32,8 @@ fn main() { //~^ ERROR type mismatch in function arguments //~| HELP call `Option::as_deref_mut()` first let _ = produces_string().and_then(generic); + + let _ = produces_string().as_deref().and_then(generic_ref); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref()` first } diff --git a/tests/ui/mismatched_types/suggest-option-asderef.rs b/tests/ui/mismatched_types/suggest-option-asderef.rs index 3cfb2ffa828c6..a5278b8fb1618 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef.rs +++ b/tests/ui/mismatched_types/suggest-option-asderef.rs @@ -16,6 +16,11 @@ fn generic(_: T) -> Option<()> { Some(()) } +fn generic_ref(_: &T) -> Option<()> { + //~^ HELP do not borrow the argument + Some(()) +} + fn main() { let _: Option<()> = produces_string().and_then(takes_str); //~^ ERROR type mismatch in function arguments @@ -27,4 +32,8 @@ fn main() { //~^ ERROR type mismatch in function arguments //~| HELP call `Option::as_deref_mut()` first let _ = produces_string().and_then(generic); + + let _ = produces_string().and_then(generic_ref); + //~^ ERROR type mismatch in function arguments + //~| HELP call `Option::as_deref()` first } diff --git a/tests/ui/mismatched_types/suggest-option-asderef.stderr b/tests/ui/mismatched_types/suggest-option-asderef.stderr index 46da19d2bf4f2..01341603dde3f 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef.stderr @@ -1,5 +1,5 @@ error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef.rs:20:52 + --> $DIR/suggest-option-asderef.rs:25:52 | LL | fn takes_str(_: &str) -> Option<()> { | ----------------------------------- found signature defined here @@ -19,7 +19,7 @@ LL | let _: Option<()> = produces_string().as_deref().and_then(takes_str); | +++++++++++ error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef.rs:23:55 + --> $DIR/suggest-option-asderef.rs:28:55 | LL | fn takes_str(_: &str) -> Option<()> { | ----------------------------------- found signature defined here @@ -39,7 +39,7 @@ LL | let _: Option> = produces_string().as_deref().map(takes_str) | +++++++++++ error[E0631]: type mismatch in function arguments - --> $DIR/suggest-option-asderef.rs:26:55 + --> $DIR/suggest-option-asderef.rs:31:55 | LL | fn takes_str_mut(_: &mut str) -> Option<()> { | ------------------------------------------- found signature defined here @@ -58,6 +58,31 @@ help: call `Option::as_deref_mut()` first LL | let _: Option> = produces_string().as_deref_mut().map(takes_str_mut); | +++++++++++++++ -error: aborting due to 3 previous errors +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef.rs:36:40 + | +LL | fn generic_ref(_: &T) -> Option<()> { + | -------------------------------------- found signature defined here +... +LL | let _ = produces_string().and_then(generic_ref); + | -------- ^^^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn(String) -> _` + found function signature `for<'a> fn(&'a _) -> _` +note: required by a bound in `Option::::and_then` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: do not borrow the argument + | +LL - fn generic_ref(_: &T) -> Option<()> { +LL + fn generic_ref(_: T) -> Option<()> { + | +help: call `Option::as_deref()` first + | +LL | let _ = produces_string().as_deref().and_then(generic_ref); + | +++++++++++ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0631`. From c12575d3173eb6ba985482808a87e54f23ad328e Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Mon, 5 Jun 2023 08:34:06 +0000 Subject: [PATCH 2/2] Normalize in infcx instead of globally for `Option::as_deref` suggestion The projection may contain inference variables. These inference variables are local to the local inference context. Using `tcx.normalize_erasing_regions` doesn't work here because this method is global and does not have access to the inference context. It's therefore unable to deal with the inference variables. We normalize in the local inference context instead, which knowns about the inference variables. --- .../src/traits/error_reporting/suggestions.rs | 3 ++- .../suggest-option-asderef-inference-var.rs | 9 +++++++ ...uggest-option-asderef-inference-var.stderr | 24 +++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 tests/ui/mismatched_types/suggest-option-asderef-inference-var.rs create mode 100644 tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 4ca83904f4754..80d0faca670a7 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -3592,7 +3592,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Extract `::Target` assoc type and check that it is `T` && let Some(deref_target_did) = tcx.lang_items().deref_target() && let projection = tcx.mk_projection(deref_target_did, tcx.mk_substs(&[ty::GenericArg::from(found_ty)])) - && let Ok(deref_target) = tcx.try_normalize_erasing_regions(param_env, projection) + && let InferOk { value: deref_target, obligations } = infcx.at(&ObligationCause::dummy(), param_env).normalize(projection) + && obligations.iter().all(|obligation| infcx.predicate_must_hold_modulo_regions(obligation)) && infcx.can_eq(param_env, deref_target, target_ty) { let help = if let hir::Mutability::Mut = needs_mut diff --git a/tests/ui/mismatched_types/suggest-option-asderef-inference-var.rs b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.rs new file mode 100644 index 0000000000000..5febbbe392b24 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.rs @@ -0,0 +1,9 @@ +fn deref_int(a: &i32) -> i32 { + *a +} + +fn main() { + // https://github.com/rust-lang/rust/issues/112293 + let _has_inference_vars: Option = Some(0).map(deref_int); + //~^ ERROR type mismatch in function arguments +} diff --git a/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr new file mode 100644 index 0000000000000..71c4729e31038 --- /dev/null +++ b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr @@ -0,0 +1,24 @@ +error[E0631]: type mismatch in function arguments + --> $DIR/suggest-option-asderef-inference-var.rs:7:56 + | +LL | fn deref_int(a: &i32) -> i32 { + | ---------------------------- found signature defined here +... +LL | let _has_inference_vars: Option = Some(0).map(deref_int); + | --- ^^^^^^^^^ expected due to this + | | + | required by a bound introduced by this call + | + = note: expected function signature `fn({integer}) -> _` + found function signature `for<'a> fn(&'a i32) -> _` +note: required by a bound in `Option::::map` + --> $SRC_DIR/core/src/option.rs:LL:COL +help: do not borrow the argument + | +LL - fn deref_int(a: &i32) -> i32 { +LL + fn deref_int(a: i32) -> i32 { + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0631`.