Skip to content

Commit 22e49e2

Browse files
committed
Improve non-closure, reference in-and-out errors.
For cases where there are references in the parameters and in the the outputs that do not match, and where no closures are involved, this commit introduces an improved error that mentions (or synthesizes) a name for the regions involved to better illustrate why the borrow does not live long enough.
1 parent 650a61c commit 22e49e2

File tree

2 files changed

+114
-10
lines changed

2 files changed

+114
-10
lines changed

src/librustc_mir/borrow_check/error_reporting.rs

Lines changed: 106 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use rustc::mir::{
1919
VarBindingForm,
2020
};
2121
use rustc::ty;
22+
use rustc::hir;
2223
use rustc_data_structures::fx::FxHashSet;
2324
use rustc_data_structures::sync::Lrc;
2425
use rustc_errors::{Applicability, DiagnosticBuilder};
@@ -462,9 +463,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
462463
}
463464

464465
let mut err = match &self.describe_place(&borrow.borrowed_place) {
465-
Some(_) if self.is_place_thread_local(root_place) => {
466-
self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span)
467-
}
466+
Some(_) if self.is_place_thread_local(root_place) =>
467+
self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span),
468468
Some(name) => self.report_local_value_does_not_live_long_enough(
469469
context,
470470
name,
@@ -511,14 +511,50 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
511511
Origin::Mir,
512512
);
513513

514-
err.span_label(borrow_span, "borrowed value does not live long enough");
515-
err.span_label(
516-
drop_span,
517-
format!("`{}` dropped here while still borrowed", name),
518-
);
514+
let explanation = self.explain_why_borrow_contains_point(context, borrow, kind_place);
515+
if let Some((
516+
arg_name,
517+
arg_span,
518+
return_name,
519+
return_span,
520+
)) = self.find_name_and_ty_of_values() {
521+
err.span_label(
522+
arg_span,
523+
format!("has lifetime `{}`", arg_name)
524+
);
519525

520-
self.explain_why_borrow_contains_point(context, borrow, kind_place)
521-
.emit(self.infcx.tcx, &mut err);
526+
err.span_label(
527+
return_span,
528+
format!(
529+
"{}has lifetime `{}`",
530+
if arg_name == return_name { "also " } else { "" },
531+
return_name
532+
)
533+
);
534+
535+
err.span_label(
536+
borrow_span,
537+
format!("`{}` would have to be valid for `{}`", name, return_name),
538+
);
539+
540+
err.span_label(
541+
drop_span,
542+
format!("but `{}` dropped here while still borrowed", name),
543+
);
544+
545+
if let BorrowExplanation::MustBeValidFor(..) = explanation { } else {
546+
explanation.emit(self.infcx.tcx, &mut err);
547+
}
548+
} else {
549+
err.span_label(borrow_span, "borrowed value does not live long enough");
550+
551+
err.span_label(
552+
drop_span,
553+
format!("`{}` dropped here while still borrowed", name),
554+
);
555+
556+
explanation.emit(self.infcx.tcx, &mut err);
557+
}
522558

523559
err
524560
}
@@ -645,6 +681,66 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
645681
err
646682
}
647683

684+
fn find_name_and_ty_of_values(
685+
&self,
686+
) -> Option<(String, Span, String, Span)> {
687+
let mir_node_id = self.infcx.tcx.hir.as_local_node_id(self.mir_def_id)?;
688+
let fn_decl = self.infcx.tcx.hir.fn_decl(mir_node_id)?;
689+
690+
// If there is one argument and this error is being reported, that means
691+
// that the lifetime of the borrow could not be made to match the single
692+
// argument's lifetime. We can highlight it.
693+
//
694+
// If there is more than one argument and this error is being reported, that
695+
// means there must be a self parameter - as otherwise there would be an error
696+
// from lifetime elision and not this. So we highlight the self parameter.
697+
let arg = fn_decl.inputs
698+
.first()
699+
.and_then(|ty| {
700+
if let hir::TyKind::Rptr(
701+
hir::Lifetime { name, span, .. }, ..
702+
) = ty.node {
703+
Some((name, span))
704+
} else {
705+
None
706+
}
707+
});
708+
709+
let ret = if let hir::FunctionRetTy::Return(ret_ty) = &fn_decl.output {
710+
if let hir::Ty {
711+
node: hir::TyKind::Rptr(hir::Lifetime { name, span, .. }, ..),
712+
..
713+
} = ret_ty.clone().into_inner() {
714+
Some((name, span))
715+
} else {
716+
None
717+
}
718+
} else {
719+
None
720+
};
721+
722+
let (arg_name, arg_span) = arg?;
723+
let (return_name, return_span) = ret?;
724+
725+
let lifetimes_match = arg_name == return_name;
726+
727+
let arg_name = if arg_name.is_elided() {
728+
"'0".to_string()
729+
} else {
730+
format!("{}", arg_name.ident())
731+
};
732+
733+
let return_name = if return_name.is_elided() && lifetimes_match {
734+
"'0".to_string()
735+
} else if return_name.is_elided() {
736+
"'1".to_string()
737+
} else {
738+
format!("{}", return_name.ident())
739+
};
740+
741+
Some((arg_name, arg_span, return_name, return_span))
742+
}
743+
648744
fn get_moved_indexes(&mut self, context: Context, mpi: MovePathIndex) -> Vec<MoveOutIndex> {
649745
let mir = self.mir;
650746

src/librustc_mir/borrow_check/nll/region_infer/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
652652
}
653653
}
654654

655+
pub fn universal_regions_outlived_by<'a>(
656+
&'a self,
657+
r: RegionVid
658+
) -> impl Iterator<Item = RegionVid> + 'a {
659+
let borrow_scc = self.constraint_sccs.scc(r);
660+
self.scc_values.universal_regions_outlived_by(borrow_scc)
661+
}
662+
655663
/// Invoked when we have some type-test (e.g., `T: 'X`) that we cannot
656664
/// prove to be satisfied. If this is a closure, we will attempt to
657665
/// "promote" this type-test into our `ClosureRegionRequirements` and

0 commit comments

Comments
 (0)