Skip to content

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

+3
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

+18
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

+116-1
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

+1-1
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

+52-48
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)