Rollup merge of #134259 - compiler-errors:infer-ret-ty, r=dtolnay
Clean up `infer_return_ty_for_fn_sig` The code for lowering fn signatures from HIR currently is structured to prefer the recovery path (where users write `-> _`) over the good path. This PR pulls the recovery code out into a separate fn. Review w/o whitespace
This commit is contained in:
commit
1fa1f3001f
1 changed files with 95 additions and 86 deletions
|
@ -1340,7 +1340,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
|
|||
..
|
||||
})
|
||||
| Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => {
|
||||
infer_return_ty_for_fn_sig(sig, generics, def_id, &icx)
|
||||
lower_fn_sig_recovering_infer_ret_ty(&icx, sig, generics, def_id)
|
||||
}
|
||||
|
||||
ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
|
||||
|
@ -1357,7 +1357,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
|
|||
None,
|
||||
)
|
||||
} else {
|
||||
infer_return_ty_for_fn_sig(sig, generics, def_id, &icx)
|
||||
lower_fn_sig_recovering_infer_ret_ty(&icx, sig, generics, def_id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1407,99 +1407,108 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
|
|||
ty::EarlyBinder::bind(output)
|
||||
}
|
||||
|
||||
fn infer_return_ty_for_fn_sig<'tcx>(
|
||||
sig: &hir::FnSig<'tcx>,
|
||||
generics: &hir::Generics<'_>,
|
||||
def_id: LocalDefId,
|
||||
fn lower_fn_sig_recovering_infer_ret_ty<'tcx>(
|
||||
icx: &ItemCtxt<'tcx>,
|
||||
sig: &'tcx hir::FnSig<'tcx>,
|
||||
generics: &'tcx hir::Generics<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
) -> ty::PolyFnSig<'tcx> {
|
||||
if let Some(infer_ret_ty) = sig.decl.output.get_infer_ret_ty() {
|
||||
return recover_infer_ret_ty(icx, infer_ret_ty, generics, def_id);
|
||||
}
|
||||
|
||||
icx.lowerer().lower_fn_ty(
|
||||
icx.tcx().local_def_id_to_hir_id(def_id),
|
||||
sig.header.safety,
|
||||
sig.header.abi,
|
||||
sig.decl,
|
||||
Some(generics),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn recover_infer_ret_ty<'tcx>(
|
||||
icx: &ItemCtxt<'tcx>,
|
||||
infer_ret_ty: &'tcx hir::Ty<'tcx>,
|
||||
generics: &'tcx hir::Generics<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
) -> ty::PolyFnSig<'tcx> {
|
||||
let tcx = icx.tcx;
|
||||
let hir_id = tcx.local_def_id_to_hir_id(def_id);
|
||||
|
||||
match sig.decl.output.get_infer_ret_ty() {
|
||||
Some(ty) => {
|
||||
let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
|
||||
// Typeck doesn't expect erased regions to be returned from `type_of`.
|
||||
// This is a heuristic approach. If the scope has region parameters,
|
||||
// we should change fn_sig's lifetime from `ReErased` to `ReError`,
|
||||
// otherwise to `ReStatic`.
|
||||
let has_region_params = generics.params.iter().any(|param| match param.kind {
|
||||
GenericParamKind::Lifetime { .. } => true,
|
||||
_ => false,
|
||||
});
|
||||
let fn_sig = fold_regions(tcx, fn_sig, |r, _| match *r {
|
||||
ty::ReErased => {
|
||||
if has_region_params {
|
||||
ty::Region::new_error_with_message(
|
||||
tcx,
|
||||
DUMMY_SP,
|
||||
"erased region is not allowed here in return type",
|
||||
)
|
||||
} else {
|
||||
tcx.lifetimes.re_static
|
||||
}
|
||||
}
|
||||
_ => r,
|
||||
});
|
||||
let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
|
||||
|
||||
let mut visitor = HirPlaceholderCollector::default();
|
||||
visitor.visit_ty(ty);
|
||||
|
||||
let mut diag = bad_placeholder(icx.lowerer(), visitor.0, "return type");
|
||||
let ret_ty = fn_sig.output();
|
||||
// Don't leak types into signatures unless they're nameable!
|
||||
// For example, if a function returns itself, we don't want that
|
||||
// recursive function definition to leak out into the fn sig.
|
||||
let mut recovered_ret_ty = None;
|
||||
|
||||
if let Some(suggestable_ret_ty) = ret_ty.make_suggestable(tcx, false, None) {
|
||||
diag.span_suggestion(
|
||||
ty.span,
|
||||
"replace with the correct return type",
|
||||
suggestable_ret_ty,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
recovered_ret_ty = Some(suggestable_ret_ty);
|
||||
} else if let Some(sugg) = suggest_impl_trait(
|
||||
&tcx.infer_ctxt().build(TypingMode::non_body_analysis()),
|
||||
tcx.param_env(def_id),
|
||||
ret_ty,
|
||||
) {
|
||||
diag.span_suggestion(
|
||||
ty.span,
|
||||
"replace with an appropriate return type",
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if ret_ty.is_closure() {
|
||||
diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
|
||||
// Typeck doesn't expect erased regions to be returned from `type_of`.
|
||||
// This is a heuristic approach. If the scope has region parameters,
|
||||
// we should change fn_sig's lifetime from `ReErased` to `ReError`,
|
||||
// otherwise to `ReStatic`.
|
||||
let has_region_params = generics.params.iter().any(|param| match param.kind {
|
||||
GenericParamKind::Lifetime { .. } => true,
|
||||
_ => false,
|
||||
});
|
||||
let fn_sig = fold_regions(tcx, fn_sig, |r, _| match *r {
|
||||
ty::ReErased => {
|
||||
if has_region_params {
|
||||
ty::Region::new_error_with_message(
|
||||
tcx,
|
||||
DUMMY_SP,
|
||||
"erased region is not allowed here in return type",
|
||||
)
|
||||
} else {
|
||||
tcx.lifetimes.re_static
|
||||
}
|
||||
// Also note how `Fn` traits work just in case!
|
||||
if ret_ty.is_closure() {
|
||||
diag.note(
|
||||
"for more information on `Fn` traits and closure types, see \
|
||||
https://doc.rust-lang.org/book/ch13-01-closures.html",
|
||||
);
|
||||
}
|
||||
|
||||
let guar = diag.emit();
|
||||
ty::Binder::dummy(tcx.mk_fn_sig(
|
||||
fn_sig.inputs().iter().copied(),
|
||||
recovered_ret_ty.unwrap_or_else(|| Ty::new_error(tcx, guar)),
|
||||
fn_sig.c_variadic,
|
||||
fn_sig.safety,
|
||||
fn_sig.abi,
|
||||
))
|
||||
}
|
||||
None => icx.lowerer().lower_fn_ty(
|
||||
hir_id,
|
||||
sig.header.safety,
|
||||
sig.header.abi,
|
||||
sig.decl,
|
||||
Some(generics),
|
||||
None,
|
||||
),
|
||||
_ => r,
|
||||
});
|
||||
|
||||
let mut visitor = HirPlaceholderCollector::default();
|
||||
visitor.visit_ty(infer_ret_ty);
|
||||
|
||||
let mut diag = bad_placeholder(icx.lowerer(), visitor.0, "return type");
|
||||
let ret_ty = fn_sig.output();
|
||||
|
||||
// Don't leak types into signatures unless they're nameable!
|
||||
// For example, if a function returns itself, we don't want that
|
||||
// recursive function definition to leak out into the fn sig.
|
||||
let mut recovered_ret_ty = None;
|
||||
if let Some(suggestable_ret_ty) = ret_ty.make_suggestable(tcx, false, None) {
|
||||
diag.span_suggestion(
|
||||
infer_ret_ty.span,
|
||||
"replace with the correct return type",
|
||||
suggestable_ret_ty,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
recovered_ret_ty = Some(suggestable_ret_ty);
|
||||
} else if let Some(sugg) = suggest_impl_trait(
|
||||
&tcx.infer_ctxt().build(TypingMode::non_body_analysis()),
|
||||
tcx.param_env(def_id),
|
||||
ret_ty,
|
||||
) {
|
||||
diag.span_suggestion(
|
||||
infer_ret_ty.span,
|
||||
"replace with an appropriate return type",
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if ret_ty.is_closure() {
|
||||
diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
|
||||
}
|
||||
|
||||
// Also note how `Fn` traits work just in case!
|
||||
if ret_ty.is_closure() {
|
||||
diag.note(
|
||||
"for more information on `Fn` traits and closure types, see \
|
||||
https://doc.rust-lang.org/book/ch13-01-closures.html",
|
||||
);
|
||||
}
|
||||
let guar = diag.emit();
|
||||
ty::Binder::dummy(tcx.mk_fn_sig(
|
||||
fn_sig.inputs().iter().copied(),
|
||||
recovered_ret_ty.unwrap_or_else(|| Ty::new_error(tcx, guar)),
|
||||
fn_sig.c_variadic,
|
||||
fn_sig.safety,
|
||||
fn_sig.abi,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn suggest_impl_trait<'tcx>(
|
||||
|
|
Loading…
Add table
Reference in a new issue