Skip to content

Commit dbecb7a

Browse files
committed
Auto merge of #54782 - pnkfelix:issue-54556-semi-on-tail-diagnostic, r=nikomatsakis
NLL: temps in block tail expression diagnostic This change adds a diagnostic that explains when temporaries in a block tail expression live longer than block local variables that they borrow, and attempts to suggest turning the tail expresion into a statement (either by adding a semicolon at the end, when its result value is clearly unused, or by introducing a `let`-binding for the result value and then returning that). Fix #54556
2 parents 4efdc04 + 704877f commit dbecb7a

File tree

66 files changed

+958
-141
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+958
-141
lines changed

src/librustc/ich/impls_mir.rs

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ impl_stable_hash_for!(struct mir::LocalDecl<'tcx> {
2929
source_info,
3030
visibility_scope,
3131
internal,
32+
is_block_tail,
3233
is_user_variable
3334
});
3435
impl_stable_hash_for!(struct mir::UpvarDecl { debug_name, var_hir_id, by_ref, mutability });

src/librustc/mir/mod.rs

+42-3
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,26 @@ mod binding_form_impl {
638638
}
639639
}
640640

641+
/// `BlockTailInfo` is attached to the `LocalDecl` for temporaries
642+
/// created during evaluation of expressions in a block tail
643+
/// expression; that is, a block like `{ STMT_1; STMT_2; EXPR }`.
644+
///
645+
/// It is used to improve diagnostics when such temporaries are
646+
/// involved in borrow_check errors, e.g. explanations of where the
647+
/// temporaries come from, when their destructors are run, and/or how
648+
/// one might revise the code to satisfy the borrow checker's rules.
649+
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
650+
pub struct BlockTailInfo {
651+
/// If `true`, then the value resulting from evaluating this tail
652+
/// expression is ignored by the block's expression context.
653+
///
654+
/// Examples include `{ ...; tail };` and `let _ = { ...; tail };`
655+
/// but not e.g. `let _x = { ...; tail };`
656+
pub tail_result_is_ignored: bool,
657+
}
658+
659+
impl_stable_hash_for!(struct BlockTailInfo { tail_result_is_ignored });
660+
641661
/// A MIR local.
642662
///
643663
/// This can be a binding declared by the user, a temporary inserted by the compiler, a function
@@ -677,6 +697,12 @@ pub struct LocalDecl<'tcx> {
677697
/// generator.
678698
pub internal: bool,
679699

700+
/// If this local is a temporary and `is_block_tail` is `Some`,
701+
/// then it is a temporary created for evaluation of some
702+
/// subexpression of some block's tail expression (with no
703+
/// intervening statement context).
704+
pub is_block_tail: Option<BlockTailInfo>,
705+
680706
/// Type of this local.
681707
pub ty: Ty<'tcx>,
682708

@@ -825,10 +851,19 @@ impl<'tcx> LocalDecl<'tcx> {
825851
Self::new_local(ty, Mutability::Mut, false, span)
826852
}
827853

828-
/// Create a new immutable `LocalDecl` for a temporary.
854+
/// Converts `self` into same `LocalDecl` except tagged as immutable.
855+
#[inline]
856+
pub fn immutable(mut self) -> Self {
857+
self.mutability = Mutability::Not;
858+
self
859+
}
860+
861+
/// Converts `self` into same `LocalDecl` except tagged as internal temporary.
829862
#[inline]
830-
pub fn new_immutable_temp(ty: Ty<'tcx>, span: Span) -> Self {
831-
Self::new_local(ty, Mutability::Not, false, span)
863+
pub fn block_tail(mut self, info: BlockTailInfo) -> Self {
864+
assert!(self.is_block_tail.is_none());
865+
self.is_block_tail = Some(info);
866+
self
832867
}
833868

834869
/// Create a new `LocalDecl` for a internal temporary.
@@ -856,6 +891,7 @@ impl<'tcx> LocalDecl<'tcx> {
856891
visibility_scope: OUTERMOST_SOURCE_SCOPE,
857892
internal,
858893
is_user_variable: None,
894+
is_block_tail: None,
859895
}
860896
}
861897

@@ -874,6 +910,7 @@ impl<'tcx> LocalDecl<'tcx> {
874910
},
875911
visibility_scope: OUTERMOST_SOURCE_SCOPE,
876912
internal: false,
913+
is_block_tail: None,
877914
name: None, // FIXME maybe we do want some name here?
878915
is_user_variable: None,
879916
}
@@ -2668,6 +2705,7 @@ pub enum ClosureOutlivesSubject<'tcx> {
26682705
*/
26692706

26702707
CloneTypeFoldableAndLiftImpls! {
2708+
BlockTailInfo,
26712709
Mutability,
26722710
SourceInfo,
26732711
UpvarDecl,
@@ -2711,6 +2749,7 @@ BraceStructTypeFoldableImpl! {
27112749
user_ty,
27122750
name,
27132751
source_info,
2752+
is_block_tail,
27142753
visibility_scope,
27152754
}
27162755
}

src/librustc/mir/visit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,7 @@ macro_rules! make_mir_visitor {
728728
ref $($mutability)* visibility_scope,
729729
internal: _,
730730
is_user_variable: _,
731+
is_block_tail: _,
731732
} = *local_decl;
732733

733734
self.visit_ty(ty, TyContext::LocalDecl {

src/librustc_mir/borrow_check/error_reporting.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
262262
move_spans.var_span_label(&mut err, "move occurs due to use in closure");
263263

264264
self.explain_why_borrow_contains_point(context, borrow, None)
265-
.emit(self.infcx.tcx, &mut err, String::new());
265+
.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "");
266266
err.buffer(&mut self.errors_buffer);
267267
}
268268

@@ -299,7 +299,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
299299
});
300300

301301
self.explain_why_borrow_contains_point(context, borrow, None)
302-
.emit(self.infcx.tcx, &mut err, String::new());
302+
.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "");
303303
err.buffer(&mut self.errors_buffer);
304304
}
305305

@@ -483,7 +483,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
483483
}
484484

485485
self.explain_why_borrow_contains_point(context, issued_borrow, None)
486-
.emit(self.infcx.tcx, &mut err, first_borrow_desc.to_string());
486+
.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, first_borrow_desc);
487487

488488
err.buffer(&mut self.errors_buffer);
489489
}
@@ -638,7 +638,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
638638

639639
if let BorrowExplanation::MustBeValidFor(..) = explanation {
640640
} else {
641-
explanation.emit(self.infcx.tcx, &mut err, String::new());
641+
explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "");
642642
}
643643
} else {
644644
err.span_label(borrow_span, "borrowed value does not live long enough");
@@ -649,7 +649,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
649649

650650
borrow_spans.args_span_label(&mut err, "value captured here");
651651

652-
explanation.emit(self.infcx.tcx, &mut err, String::new());
652+
explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "");
653653
}
654654

655655
err
@@ -709,7 +709,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
709709
_ => {}
710710
}
711711

712-
explanation.emit(self.infcx.tcx, &mut err, String::new());
712+
explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "");
713713

714714
err.buffer(&mut self.errors_buffer);
715715
}
@@ -770,13 +770,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
770770
match explanation {
771771
BorrowExplanation::UsedLater(..)
772772
| BorrowExplanation::UsedLaterInLoop(..)
773-
| BorrowExplanation::UsedLaterWhenDropped(..) => {
773+
| BorrowExplanation::UsedLaterWhenDropped { .. } => {
774774
// Only give this note and suggestion if it could be relevant.
775775
err.note("consider using a `let` binding to create a longer lived value");
776776
}
777777
_ => {}
778778
}
779-
explanation.emit(self.infcx.tcx, &mut err, String::new());
779+
explanation.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "");
780780

781781
borrow_spans.args_span_label(&mut err, "value captured here");
782782

@@ -913,7 +913,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
913913
loan_spans.var_span_label(&mut err, "borrow occurs due to use in closure");
914914

915915
self.explain_why_borrow_contains_point(context, loan, None)
916-
.emit(self.infcx.tcx, &mut err, String::new());
916+
.add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, "");
917917

918918
err.buffer(&mut self.errors_buffer);
919919
}

0 commit comments

Comments
 (0)