From d2fe289fc7fe15c51da2b5c5b6b87211a14720b9 Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Sat, 31 Jul 2021 15:46:03 +0200 Subject: [PATCH] Improve error reporting for closure return type mismatches --- compiler/rustc_typeck/src/check/check.rs | 2 ++ compiler/rustc_typeck/src/check/closure.rs | 15 ++++++-- compiler/rustc_typeck/src/check/demand.rs | 35 +++++++++---------- .../rustc_typeck/src/check/fn_ctxt/mod.rs | 7 ++++ compiler/rustc_typeck/src/check/mod.rs | 2 +- src/test/ui/closures/issue-87461.rs | 29 +++++++++++++++ src/test/ui/closures/issue-87461.stderr | 21 +++++++++++ 7 files changed, 89 insertions(+), 22 deletions(-) create mode 100644 src/test/ui/closures/issue-87461.rs create mode 100644 src/test/ui/closures/issue-87461.stderr diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 8eb51b977ed..5ccdfc0ed43 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -78,6 +78,7 @@ pub(super) fn check_fn<'a, 'tcx>( fn_id: hir::HirId, body: &'tcx hir::Body<'tcx>, can_be_generator: Option, + return_type_pre_known: bool, ) -> (FnCtxt<'a, 'tcx>, Option>) { let mut fn_sig = fn_sig; @@ -87,6 +88,7 @@ pub(super) fn check_fn<'a, 'tcx>( // in the case of closures, based on the outer context. let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id); fcx.ps.set(UnsafetyState::function(fn_sig.unsafety, fn_id)); + fcx.return_type_pre_known = return_type_pre_known; let tcx = fcx.tcx; let sess = tcx.sess; diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index 22d3dc6bdc0..e4b615e4ade 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -73,8 +73,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("check_closure: ty_of_closure returns {:?}", liberated_sig); - let generator_types = - check_fn(self, self.param_env, liberated_sig, decl, expr.hir_id, body, gen).1; + let return_type_pre_known = !liberated_sig.output().is_ty_infer(); + + let generator_types = check_fn( + self, + self.param_env, + liberated_sig, + decl, + expr.hir_id, + body, + gen, + return_type_pre_known, + ) + .1; let parent_substs = InternalSubsts::identity_for_item( self.tcx, diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index e5a00f70639..a4a54bac870 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -40,7 +40,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_missing_parentheses(err, expr); self.note_need_for_fn_pointer(err, expected, expr_ty); self.note_internal_mutation_in_method(err, expr, expected, expr_ty); - self.report_closure_infered_return_type(err, expected) + self.report_closure_inferred_return_type(err, expected); } // Requires that the two types unify, and prints an error message if @@ -1106,29 +1106,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Report the type inferred by the return statement. - fn report_closure_infered_return_type( + fn report_closure_inferred_return_type( &self, err: &mut DiagnosticBuilder<'_>, expected: Ty<'tcx>, ) { if let Some(sp) = self.ret_coercion_span.get() { - // If the closure has an explicit return type annotation, - // then a type error may occur at the first return expression we - // see in the closure (if it conflicts with the declared - // return type). Skip adding a note in this case, since it - // would be incorrect. - if !err.span.primary_spans().iter().any(|&span| span == sp) { - let hir = self.tcx.hir(); - let body_owner = hir.body_owned_by(hir.enclosing_body_owner(self.body_id)); - if self.tcx.is_closure(hir.body_owner_def_id(body_owner).to_def_id()) { - err.span_note( - sp, - &format!( - "return type inferred to be `{}` here", - self.resolve_vars_if_possible(expected) - ), - ); - } + // If the closure has an explicit return type annotation, or if + // the closure's return type has been inferred from outside + // requirements (such as an Fn* trait bound), then a type error + // may occur at the first return expression we see in the closure + // (if it conflicts with the declared return type). Skip adding a + // note in this case, since it would be incorrect. + if !self.return_type_pre_known { + err.span_note( + sp, + &format!( + "return type inferred to be `{}` here", + self.resolve_vars_if_possible(expected) + ), + ); } } } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs index c0ecee155c6..a466a04bd98 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs @@ -111,6 +111,12 @@ pub struct FnCtxt<'a, 'tcx> { pub(super) enclosing_breakables: RefCell>, pub(super) inh: &'a Inherited<'a, 'tcx>, + + /// True if the function or closure's return type is known before + /// entering the function/closure, i.e. if the return type is + /// either given explicitly or inferred from, say, an `Fn*` trait + /// bound. Used for diagnostic purposes only. + pub(super) return_type_pre_known: bool, } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -137,6 +143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { by_id: Default::default(), }), inh, + return_type_pre_known: true, } } diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index d1e583ed184..a44808c11b3 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -391,7 +391,7 @@ fn typeck_with_fallback<'tcx>( fn_sig, ); - let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, None).0; + let fcx = check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0; fcx } else { let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); diff --git a/src/test/ui/closures/issue-87461.rs b/src/test/ui/closures/issue-87461.rs new file mode 100644 index 00000000000..0151080eeb4 --- /dev/null +++ b/src/test/ui/closures/issue-87461.rs @@ -0,0 +1,29 @@ +// Regression test for #87461. + +// edition:2021 + +async fn func() -> Result { + let _ = async { + Err(42u64) + }.await?; + + Ok(()) + //~^ ERROR: mismatched types [E0308] +} + +async fn func2() -> Result { + Err(42u64)?; + + Ok(()) + //~^ ERROR: mismatched types [E0308] +} + +fn main() { + || -> Result { + if true { + return Err(42u64); + } + Ok(()) + //~^ ERROR: mismatched types [E0308] + }; +} diff --git a/src/test/ui/closures/issue-87461.stderr b/src/test/ui/closures/issue-87461.stderr new file mode 100644 index 00000000000..a3cff2c1212 --- /dev/null +++ b/src/test/ui/closures/issue-87461.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/issue-87461.rs:10:8 + | +LL | Ok(()) + | ^^ expected `u16`, found `()` + +error[E0308]: mismatched types + --> $DIR/issue-87461.rs:17:8 + | +LL | Ok(()) + | ^^ expected `u16`, found `()` + +error[E0308]: mismatched types + --> $DIR/issue-87461.rs:26:12 + | +LL | Ok(()) + | ^^ expected `u16`, found `()` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`.