Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit c3dbdfe

Browse files
committed
MIR changes to improve NLL cannot mutate errors
Alway use unique instead of mutable borrows immutable upvars. Mark variables that are references for a match guard
1 parent 509cbf3 commit c3dbdfe

File tree

5 files changed

+190
-50
lines changed

5 files changed

+190
-50
lines changed

src/librustc/mir/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,8 @@ pub enum BindingForm<'tcx> {
531531
Var(VarBindingForm<'tcx>),
532532
/// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit.
533533
ImplicitSelf,
534+
/// Reference used in a guard expression to ensure immutability.
535+
RefForGuard,
534536
}
535537

536538
CloneTypeFoldableAndLiftImpls! { BindingForm<'tcx>, }
@@ -555,6 +557,7 @@ mod binding_form_impl {
555557
match self {
556558
Var(binding) => binding.hash_stable(hcx, hasher),
557559
ImplicitSelf => (),
560+
RefForGuard => (),
558561
}
559562
}
560563
}

src/librustc_mir/borrow_check/error_reporting.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,24 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
742742
autoderef,
743743
&including_downcast,
744744
)?;
745+
} else if let Place::Local(local) = proj.base {
746+
if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard))
747+
= self.mir.local_decls[local].is_user_variable {
748+
self.append_place_to_string(
749+
&proj.base,
750+
buf,
751+
autoderef,
752+
&including_downcast,
753+
)?;
754+
} else {
755+
buf.push_str(&"*");
756+
self.append_place_to_string(
757+
&proj.base,
758+
buf,
759+
autoderef,
760+
&including_downcast,
761+
)?;
762+
}
745763
} else {
746764
buf.push_str(&"*");
747765
self.append_place_to_string(

src/librustc_mir/build/expr/as_rvalue.rs

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
206206
this.consume_by_copy_or_move(place)
207207
}
208208
_ => {
209-
unpack!(block = this.as_operand(block, scope, upvar))
209+
// Turn mutable borrow captures into unique
210+
// borrow captures when capturing an immutable
211+
// variable. This is sound because the mutation
212+
// that caused the capture will cause an error.
213+
match upvar.kind {
214+
ExprKind::Borrow {
215+
borrow_kind: BorrowKind::Mut {
216+
allow_two_phase_borrow: false
217+
},
218+
region,
219+
arg,
220+
} => unpack!(block = this.limit_capture_mutability(
221+
upvar.span,
222+
upvar.ty,
223+
scope,
224+
block,
225+
arg,
226+
region,
227+
)),
228+
_ => unpack!(block = this.as_operand(block, scope, upvar)),
229+
}
210230
}
211231
}
212232
})
@@ -393,6 +413,101 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
393413
}
394414
}
395415

416+
fn limit_capture_mutability(
417+
&mut self,
418+
upvar_span: Span,
419+
upvar_ty: Ty<'tcx>,
420+
temp_lifetime: Option<region::Scope>,
421+
mut block: BasicBlock,
422+
arg: ExprRef<'tcx>,
423+
region: &'tcx ty::RegionKind,
424+
) -> BlockAnd<Operand<'tcx>> {
425+
let this = self;
426+
427+
let source_info = this.source_info(upvar_span);
428+
let temp = this.local_decls.push(LocalDecl::new_temp(upvar_ty, upvar_span));
429+
430+
this.cfg.push(block, Statement {
431+
source_info,
432+
kind: StatementKind::StorageLive(temp)
433+
});
434+
435+
let arg_place = unpack!(block = this.as_place(block, arg));
436+
437+
let mutability = match arg_place {
438+
Place::Local(local) => this.local_decls[local].mutability,
439+
Place::Projection(box Projection {
440+
base: Place::Local(local),
441+
elem: ProjectionElem::Deref,
442+
}) => {
443+
debug_assert!(
444+
if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard))
445+
= this.local_decls[local].is_user_variable {
446+
true
447+
} else {
448+
false
449+
},
450+
"Unexpected capture place",
451+
);
452+
this.local_decls[local].mutability
453+
}
454+
Place::Projection(box Projection {
455+
ref base,
456+
elem: ProjectionElem::Field(upvar_index, _),
457+
})
458+
| Place::Projection(box Projection {
459+
base: Place::Projection(box Projection {
460+
ref base,
461+
elem: ProjectionElem::Field(upvar_index, _),
462+
}),
463+
elem: ProjectionElem::Deref,
464+
}) => {
465+
// Not projected from the implicit `self` in a closure.
466+
debug_assert!(
467+
match *base {
468+
Place::Local(local) => local == Local::new(1),
469+
Place::Projection(box Projection {
470+
ref base,
471+
elem: ProjectionElem::Deref,
472+
}) => *base == Place::Local(Local::new(1)),
473+
_ => false,
474+
},
475+
"Unexpected capture place"
476+
);
477+
// Not in a closure
478+
debug_assert!(
479+
this.upvar_decls.len() > upvar_index.index(),
480+
"Unexpected capture place"
481+
);
482+
this.upvar_decls[upvar_index.index()].mutability
483+
}
484+
_ => bug!("Unexpected capture place"),
485+
};
486+
487+
let borrow_kind = match mutability {
488+
Mutability::Not => BorrowKind::Unique,
489+
Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
490+
};
491+
492+
this.cfg.push_assign(
493+
block,
494+
source_info,
495+
&Place::Local(temp),
496+
Rvalue::Ref(region, borrow_kind, arg_place),
497+
);
498+
499+
// In constants, temp_lifetime is None. We should not need to drop
500+
// anything because no values with a destructor can be created in
501+
// a constant at this time, even if the type may need dropping.
502+
if let Some(temp_lifetime) = temp_lifetime {
503+
this.schedule_drop_storage_and_value(
504+
upvar_span, temp_lifetime, &Place::Local(temp), upvar_ty,
505+
);
506+
}
507+
508+
block.and(Operand::Move(Place::Local(temp)))
509+
}
510+
396511
// Helper to get a `-1` value of the appropriate type
397512
fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
398513
let param_ty = ty::ParamEnv::empty().and(self.hir.tcx().lift_to_global(&ty).unwrap());

src/librustc_mir/build/matches/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1198,7 +1198,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
11981198
visibility_scope,
11991199
// FIXME: should these secretly injected ref_for_guard's be marked as `internal`?
12001200
internal: false,
1201-
is_user_variable: None,
1201+
is_user_variable: Some(ClearCrossCrate::Set(BindingForm::RefForGuard)),
12021202
});
12031203
LocalsForNode::Three { val_for_guard, ref_for_guard, for_arm_body }
12041204
} else {

src/librustc_mir/build/mod.rs

Lines changed: 52 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
286286
/// (A match binding can have two locals; the 2nd is for the arm's guard.)
287287
var_indices: NodeMap<LocalsForNode>,
288288
local_decls: IndexVec<Local, LocalDecl<'tcx>>,
289+
upvar_decls: Vec<UpvarDecl>,
289290
unit_temp: Option<Place<'tcx>>,
290291

291292
/// cached block with the RESUME terminator; this is created
@@ -472,11 +473,52 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
472473

473474
let tcx = hir.tcx();
474475
let span = tcx.hir.span(fn_id);
476+
477+
// Gather the upvars of a closure, if any.
478+
let upvar_decls: Vec<_> = tcx.with_freevars(fn_id, |freevars| {
479+
freevars.iter().map(|fv| {
480+
let var_id = fv.var_id();
481+
let var_hir_id = tcx.hir.node_to_hir_id(var_id);
482+
let closure_expr_id = tcx.hir.local_def_id(fn_id);
483+
let capture = hir.tables().upvar_capture(ty::UpvarId {
484+
var_id: var_hir_id,
485+
closure_expr_id: LocalDefId::from_def_id(closure_expr_id),
486+
});
487+
let by_ref = match capture {
488+
ty::UpvarCapture::ByValue => false,
489+
ty::UpvarCapture::ByRef(..) => true
490+
};
491+
let mut decl = UpvarDecl {
492+
debug_name: keywords::Invalid.name(),
493+
var_hir_id: ClearCrossCrate::Set(var_hir_id),
494+
by_ref,
495+
mutability: Mutability::Not,
496+
};
497+
if let Some(hir::map::NodeBinding(pat)) = tcx.hir.find(var_id) {
498+
if let hir::PatKind::Binding(_, _, ident, _) = pat.node {
499+
decl.debug_name = ident.name;
500+
501+
if let Some(&bm) = hir.tables.pat_binding_modes().get(pat.hir_id) {
502+
if bm == ty::BindByValue(hir::MutMutable) {
503+
decl.mutability = Mutability::Mut;
504+
} else {
505+
decl.mutability = Mutability::Not;
506+
}
507+
} else {
508+
tcx.sess.delay_span_bug(pat.span, "missing binding mode");
509+
}
510+
}
511+
}
512+
decl
513+
}).collect()
514+
});
515+
475516
let mut builder = Builder::new(hir.clone(),
476517
span,
477518
arguments.len(),
478519
safety,
479-
return_ty);
520+
return_ty,
521+
upvar_decls);
480522

481523
let fn_def_id = tcx.hir.local_def_id(fn_id);
482524
let call_site_scope = region::Scope::CallSite(body.value.hir_id.local_id);
@@ -519,46 +561,7 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
519561
info!("fn_id {:?} has attrs {:?}", closure_expr_id,
520562
tcx.get_attrs(closure_expr_id));
521563

522-
// Gather the upvars of a closure, if any.
523-
let upvar_decls: Vec<_> = tcx.with_freevars(fn_id, |freevars| {
524-
freevars.iter().map(|fv| {
525-
let var_id = fv.var_id();
526-
let var_hir_id = tcx.hir.node_to_hir_id(var_id);
527-
let closure_expr_id = tcx.hir.local_def_id(fn_id);
528-
let capture = hir.tables().upvar_capture(ty::UpvarId {
529-
var_id: var_hir_id,
530-
closure_expr_id: LocalDefId::from_def_id(closure_expr_id),
531-
});
532-
let by_ref = match capture {
533-
ty::UpvarCapture::ByValue => false,
534-
ty::UpvarCapture::ByRef(..) => true
535-
};
536-
let mut decl = UpvarDecl {
537-
debug_name: keywords::Invalid.name(),
538-
var_hir_id: ClearCrossCrate::Set(var_hir_id),
539-
by_ref,
540-
mutability: Mutability::Not,
541-
};
542-
if let Some(hir::map::NodeBinding(pat)) = tcx.hir.find(var_id) {
543-
if let hir::PatKind::Binding(_, _, ident, _) = pat.node {
544-
decl.debug_name = ident.name;
545-
546-
if let Some(&bm) = hir.tables.pat_binding_modes().get(pat.hir_id) {
547-
if bm == ty::BindByValue(hir::MutMutable) {
548-
decl.mutability = Mutability::Mut;
549-
} else {
550-
decl.mutability = Mutability::Not;
551-
}
552-
} else {
553-
tcx.sess.delay_span_bug(pat.span, "missing binding mode");
554-
}
555-
}
556-
}
557-
decl
558-
}).collect()
559-
});
560-
561-
let mut mir = builder.finish(upvar_decls, yield_ty);
564+
let mut mir = builder.finish(yield_ty);
562565
mir.spread_arg = spread_arg;
563566
mir
564567
}
@@ -571,7 +574,7 @@ fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
571574
let ty = hir.tables().expr_ty_adjusted(ast_expr);
572575
let owner_id = tcx.hir.body_owner(body_id);
573576
let span = tcx.hir.span(owner_id);
574-
let mut builder = Builder::new(hir.clone(), span, 0, Safety::Safe, ty);
577+
let mut builder = Builder::new(hir.clone(), span, 0, Safety::Safe, ty, vec![]);
575578

576579
let mut block = START_BLOCK;
577580
let expr = builder.hir.mirror(ast_expr);
@@ -590,7 +593,7 @@ fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
590593
TerminatorKind::Unreachable);
591594
}
592595

593-
builder.finish(vec![], None)
596+
builder.finish(None)
594597
}
595598

596599
fn construct_error<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
@@ -599,18 +602,19 @@ fn construct_error<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
599602
let owner_id = hir.tcx().hir.body_owner(body_id);
600603
let span = hir.tcx().hir.span(owner_id);
601604
let ty = hir.tcx().types.err;
602-
let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty);
605+
let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty, vec![]);
603606
let source_info = builder.source_info(span);
604607
builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
605-
builder.finish(vec![], None)
608+
builder.finish(None)
606609
}
607610

608611
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
609612
fn new(hir: Cx<'a, 'gcx, 'tcx>,
610613
span: Span,
611614
arg_count: usize,
612615
safety: Safety,
613-
return_ty: Ty<'tcx>)
616+
return_ty: Ty<'tcx>,
617+
upvar_decls: Vec<UpvarDecl>)
614618
-> Builder<'a, 'gcx, 'tcx> {
615619
let lint_level = LintLevel::Explicit(hir.root_lint_level);
616620
let mut builder = Builder {
@@ -628,6 +632,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
628632
breakable_scopes: vec![],
629633
local_decls: IndexVec::from_elem_n(LocalDecl::new_return_place(return_ty,
630634
span), 1),
635+
upvar_decls,
631636
var_indices: NodeMap(),
632637
unit_temp: None,
633638
cached_resume_block: None,
@@ -645,7 +650,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
645650
}
646651

647652
fn finish(self,
648-
upvar_decls: Vec<UpvarDecl>,
649653
yield_ty: Option<Ty<'tcx>>)
650654
-> Mir<'tcx> {
651655
for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
@@ -661,7 +665,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
661665
yield_ty,
662666
self.local_decls,
663667
self.arg_count,
664-
upvar_decls,
668+
self.upvar_decls,
665669
self.fn_span
666670
)
667671
}

0 commit comments

Comments
 (0)