Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 94 additions & 85 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1330,7 +1330,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, .. }) => {
Expand All @@ -1347,7 +1347,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)
}
}

Expand Down Expand Up @@ -1397,99 +1397,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<'_>,
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");
}
// 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",
);
// 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
}

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>(
Expand Down
Loading