Skip to content

Commit 66e9f88

Browse files
Handle Rvalue::Ref in one place
1 parent 6596165 commit 66e9f88

File tree

1 file changed

+106
-71
lines changed

1 file changed

+106
-71
lines changed

src/librustc_mir/transform/check_consts/validation.rs

Lines changed: 106 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -322,20 +322,9 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
322322
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
323323
trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
324324

325-
// Check nested operands and places.
325+
// Special-case reborrows to be more like a copy of a reference.
326326
if let Rvalue::Ref(_, kind, ref place) = *rvalue {
327-
// Special-case reborrows to be more like a copy of a reference.
328-
let mut reborrow_place = None;
329-
if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
330-
if elem == ProjectionElem::Deref {
331-
let base_ty = Place::ty_from(&place.base, proj_base, self.body, self.tcx).ty;
332-
if let ty::Ref(..) = base_ty.kind {
333-
reborrow_place = Some(proj_base);
334-
}
335-
}
336-
}
337-
338-
if let Some(proj) = reborrow_place {
327+
if let Some(reborrowed_proj) = place_as_reborrow(self.tcx, self.body, place) {
339328
let ctx = match kind {
340329
BorrowKind::Shared => PlaceContext::NonMutatingUse(
341330
NonMutatingUseContext::SharedBorrow,
@@ -351,14 +340,13 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
351340
),
352341
};
353342
self.visit_place_base(&place.base, ctx, location);
354-
self.visit_projection(&place.base, proj, ctx, location);
355-
} else {
356-
self.super_rvalue(rvalue, location);
343+
self.visit_projection(&place.base, reborrowed_proj, ctx, location);
344+
return;
357345
}
358-
} else {
359-
self.super_rvalue(rvalue, location);
360346
}
361347

348+
self.super_rvalue(rvalue, location);
349+
362350
match *rvalue {
363351
Rvalue::Use(_) |
364352
Rvalue::Repeat(..) |
@@ -369,9 +357,87 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
369357
Rvalue::Cast(CastKind::Pointer(_), ..) |
370358
Rvalue::Discriminant(..) |
371359
Rvalue::Len(_) |
372-
Rvalue::Ref(..) |
373360
Rvalue::Aggregate(..) => {}
374361

362+
| Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place)
363+
| Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place)
364+
=> {
365+
let ty = place.ty(self.body, self.tcx).ty;
366+
let is_allowed = match ty.kind {
367+
// Inside a `static mut`, `&mut [...]` is allowed.
368+
ty::Array(..) | ty::Slice(_) if self.const_kind() == ConstKind::StaticMut
369+
=> true,
370+
371+
// FIXME(ecstaticmorse): We could allow `&mut []` inside a const context given
372+
// that this is merely a ZST and it is already eligible for promotion.
373+
// This may require an RFC?
374+
/*
375+
ty::Array(_, len) if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
376+
=> true,
377+
*/
378+
379+
_ => false,
380+
};
381+
382+
if !is_allowed {
383+
self.check_op(ops::MutBorrow(kind));
384+
}
385+
}
386+
387+
// Taking a shared borrow of a `static` is always legal, even if that `static` has
388+
// interior mutability.
389+
| Rvalue::Ref(_, BorrowKind::Shared, ref place)
390+
| Rvalue::Ref(_, BorrowKind::Shallow, ref place)
391+
if matches!(place.base, PlaceBase::Static(_))
392+
=> {}
393+
394+
| Rvalue::Ref(_, kind @ BorrowKind::Shared, ref place)
395+
| Rvalue::Ref(_, kind @ BorrowKind::Shallow, ref place)
396+
=> {
397+
// FIXME: Change the `in_*` methods to take a `FnMut` so we don't have to manually
398+
// seek the cursors beforehand.
399+
self.qualifs.has_mut_interior.cursor.seek_before(location);
400+
self.qualifs.indirectly_mutable.seek(location);
401+
402+
let borrowed_place_has_mut_interior = HasMutInterior::in_place(
403+
&self.item,
404+
&|local| self.qualifs.has_mut_interior_eager_seek(local),
405+
place.as_ref(),
406+
);
407+
408+
if borrowed_place_has_mut_interior {
409+
let src_derived_from_illegal_borrow = borrowed_place
410+
.as_local()
411+
.map_or(false, |local| self.derived_from_illegal_borrow.contains(local));
412+
413+
// Don't emit errors for borrows of values that are *themselves* the result of
414+
// an illegal borrow (e.g., the outermost `&` in `&&Cell::new(42)`). We want to
415+
// point the user to the place where the original illegal borrow occurred, not
416+
// to subsequent borrows of the resulting value.
417+
let dest_derived_from_illegal_borrow = if !src_derived_from_illegal_borrow {
418+
self.check_op(ops::MutBorrow(kind)) == CheckOpResult::Forbidden
419+
} else {
420+
true
421+
};
422+
423+
// When the target of the assignment is a local with no projections, it will be
424+
// marked as derived from an illegal borrow if necessary.
425+
//
426+
// FIXME: should we also clear `derived_from_illegal_borrow` when a local is
427+
// assigned a new value?
428+
429+
if dest_derived_from_illegal_borrow {
430+
let block = &self.body[location.block];
431+
let statement = &block.statements[location.statement_index];
432+
if let StatementKind::Assign(box (dest, _)) = &statement.kind {
433+
if let Some(dest) = dest.as_local() {
434+
self.derived_from_illegal_borrow.insert(dest);
435+
}
436+
}
437+
}
438+
}
439+
}
440+
375441
Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
376442
let operand_ty = operand.ty(self.body, self.tcx);
377443
let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
@@ -436,58 +502,6 @@ impl Visitor<'tcx> for Validator<'_, 'mir, 'tcx> {
436502
}
437503
}
438504

439-
fn visit_assign(&mut self, dest: &Place<'tcx>, rvalue: &Rvalue<'tcx>, location: Location) {
440-
trace!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
441-
442-
// Error on mutable borrows or shared borrows of values with interior mutability.
443-
//
444-
// This replicates the logic at the start of `assign` in the old const checker. Note that
445-
// it depends on `HasMutInterior` being set for mutable borrows as well as values with
446-
// interior mutability.
447-
if let Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
448-
// FIXME: Change the `in_*` methods to take a `FnMut` so we don't have to manually seek
449-
// the cursors beforehand.
450-
self.qualifs.has_mut_interior.cursor.seek_before(location);
451-
self.qualifs.indirectly_mutable.seek(location);
452-
453-
let rvalue_has_mut_interior = HasMutInterior::in_rvalue(
454-
&self.item,
455-
&|local| self.qualifs.has_mut_interior_eager_seek(local),
456-
rvalue,
457-
);
458-
459-
if rvalue_has_mut_interior {
460-
let is_derived_from_illegal_borrow = match borrowed_place.as_local() {
461-
// If an unprojected local was borrowed and its value was the result of an
462-
// illegal borrow, suppress this error and mark the result of this borrow as
463-
// illegal as well.
464-
Some(borrowed_local)
465-
if self.derived_from_illegal_borrow.contains(borrowed_local) =>
466-
{
467-
true
468-
}
469-
470-
// Otherwise proceed normally: check the legality of a mutable borrow in this
471-
// context.
472-
_ => self.check_op(ops::MutBorrow(kind)) == CheckOpResult::Forbidden,
473-
};
474-
475-
// When the target of the assignment is a local with no projections, mark it as
476-
// derived from an illegal borrow if necessary.
477-
//
478-
// FIXME: should we also clear `derived_from_illegal_borrow` when a local is
479-
// assigned a new value?
480-
if is_derived_from_illegal_borrow {
481-
if let Some(dest) = dest.as_local() {
482-
self.derived_from_illegal_borrow.insert(dest);
483-
}
484-
}
485-
}
486-
}
487-
488-
self.super_assign(dest, rvalue, location);
489-
}
490-
491505
fn visit_projection_elem(
492506
&mut self,
493507
place_base: &PlaceBase<'tcx>,
@@ -725,3 +739,24 @@ fn check_return_ty_is_sync(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, hir_id: HirId)
725739
}
726740
});
727741
}
742+
743+
fn place_as_reborrow(
744+
tcx: TyCtxt<'tcx>,
745+
body: &Body<'tcx>,
746+
place: &'a Place<'tcx>,
747+
) -> Option<&'a [PlaceElem<'tcx>]> {
748+
place
749+
.projection
750+
.split_last()
751+
.and_then(|(outermost, inner)| {
752+
if outermost != &ProjectionElem::Deref {
753+
return None;
754+
}
755+
756+
let inner_ty = Place::ty_from(&place.base, inner, body, tcx).ty;
757+
match inner_ty.kind {
758+
ty::Ref(..) => Some(inner),
759+
_ => None,
760+
}
761+
})
762+
}

0 commit comments

Comments
 (0)