Improve error reporting for closure return type mismatches

This commit is contained in:
Fabian Wolff 2021-07-31 15:46:03 +02:00
parent 914a1e2c51
commit d2fe289fc7
7 changed files with 89 additions and 22 deletions

View File

@ -78,6 +78,7 @@ pub(super) fn check_fn<'a, 'tcx>(
fn_id: hir::HirId,
body: &'tcx hir::Body<'tcx>,
can_be_generator: Option<hir::Movability>,
return_type_pre_known: bool,
) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) {
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;

View File

@ -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,

View File

@ -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)
),
);
}
}
}

View File

@ -111,6 +111,12 @@ pub struct FnCtxt<'a, 'tcx> {
pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
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,
}
}

View File

@ -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);

View File

@ -0,0 +1,29 @@
// Regression test for #87461.
// edition:2021
async fn func() -> Result<u16, u64> {
let _ = async {
Err(42u64)
}.await?;
Ok(())
//~^ ERROR: mismatched types [E0308]
}
async fn func2() -> Result<u16, u64> {
Err(42u64)?;
Ok(())
//~^ ERROR: mismatched types [E0308]
}
fn main() {
|| -> Result<u16, u64> {
if true {
return Err(42u64);
}
Ok(())
//~^ ERROR: mismatched types [E0308]
};
}

View File

@ -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`.