const_mut_refs: allow mutable refs to statics · rust-lang/rust@340f8aa (original) (raw)
`@@ -344,7 +344,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
`
344
344
` visitor.visit_ty(ty);
`
345
345
`}
`
346
346
``
347
``
`-
fn check_mut_borrow(&mut self, local: Local, kind: hir::BorrowKind) {
`
``
347
`+
fn check_mut_borrow(&mut self, place: &Place<'_>, kind: hir::BorrowKind) {
`
348
348
`match self.const_kind() {
`
349
349
`// In a const fn all borrows are transient or point to the places given via
`
350
350
`// references in the arguments (so we already checked them with
`
`@@ -355,10 +355,19 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
`
355
355
`// to mutable memory.
`
356
356
` hir::ConstContext::ConstFn => self.check_op(ops::TransientMutBorrow(kind)),
`
357
357
` _ => {
`
``
358
`+
// For indirect places, we are not creating a new permanent borrow, it's just as
`
``
359
`+
// transient as the already existing one. For reborrowing references this is handled
`
``
360
`` +
// at the top of visit_rvalue
, but for raw pointers we handle it here.
``
``
361
`` +
// Pointers/references to static mut
and cases where the *
is not the first
``
``
362
`+
// projection also end up here.
`
358
363
`// Locals with StorageDead do not live beyond the evaluation and can
`
359
364
`// thus safely be borrowed without being able to be leaked to the final
`
360
365
`// value of the constant.
`
361
``
`-
if self.local_has_storage_dead(local) {
`
``
366
`` +
// Note: This is only sound if every local that has a StorageDead
has a
``
``
367
`` +
// StorageDead
in every control flow path leading to a return
terminator.
``
``
368
`+
// The good news is that interning will detect if any unexpected mutable
`
``
369
`+
// pointer slips through.
`
``
370
`+
if place.is_indirect() || self.local_has_storage_dead(place.local) {
`
362
371
`self.check_op(ops::TransientMutBorrow(kind));
`
363
372
`} else {
`
364
373
`self.check_op(ops::MutBorrow(kind));
`
`@@ -390,6 +399,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
`
390
399
`trace!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
`
391
400
``
392
401
`// Special-case reborrows to be more like a copy of a reference.
`
``
402
`` +
// FIXME: this does not actually handle all reborrows. It only detects cases where *
is the outermost
``
``
403
`` +
// projection of the borrowed place, it skips deref'ing raw pointers and it skips static
.
``
``
404
`+
// All those cases are handled below with shared/mutable borrows.
`
``
405
`` +
// Once const_mut_refs
is stable, we should be able to entirely remove this special case.
``
``
406
`` +
// (const_refs_to_cell
is not needed, we already allow all borrows of indirect places anyway.)
``
393
407
`match *rvalue {
`
394
408
`Rvalue::Ref(_, kind, place) => {
`
395
409
`if let Some(reborrowed_place_ref) = place_as_reborrow(self.tcx, self.body, place) {
`
`@@ -460,7 +474,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
`
460
474
``
461
475
`if !is_allowed {
`
462
476
`self.check_mut_borrow(
`
463
``
`-
place.local,
`
``
477
`+
place,
`
464
478
`if matches!(rvalue, Rvalue::Ref(..)) {
`
465
479
` hir::BorrowKind::Ref
`
466
480
`} else {
`
`@@ -478,7 +492,14 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
`
478
492
` place.as_ref(),
`
479
493
`);
`
480
494
``
481
``
`-
if borrowed_place_has_mut_interior {
`
``
495
`+
// If the place is indirect, this is basically a reborrow. We have a reborrow
`
``
496
`` +
// special case above, but for raw pointers and pointers/references to static
and
``
``
497
`` +
// when the *
is not the first projection, place_as_reborrow
does not recognize
``
``
498
`+
// them as such, so we end up here. This should probably be considered a
`
``
499
`` +
// TransientCellBorrow
(we consider the equivalent mutable case a
``
``
500
`` +
// TransientMutBorrow
), but such reborrows got accidentally stabilized already and
``
``
501
`+
// it is too much of a breaking change to take back.
`
``
502
`+
if borrowed_place_has_mut_interior && !place.is_indirect() {
`
482
503
`match self.const_kind() {
`
483
504
`// In a const fn all borrows are transient or point to the places given via
`
484
505
`// references in the arguments (so we already checked them with
`
`@@ -495,6 +516,8 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
`
495
516
`// final value.
`
496
517
`` // Note: This is only sound if every local that has a StorageDead
has a
``
497
518
`` // StorageDead
in every control flow path leading to a return
terminator.
``
``
519
`+
// The good news is that interning will detect if any unexpected mutable
`
``
520
`+
// pointer slips through.
`
498
521
`if self.local_has_storage_dead(place.local) {
`
499
522
`self.check_op(ops::TransientCellBorrow);
`
500
523
`} else {
`
`@@ -948,6 +971,12 @@ fn place_as_reborrow<'tcx>(
`
948
971
`) -> Option<PlaceRef<'tcx>> {
`
949
972
`match place.as_ref().last_projection() {
`
950
973
`Some((place_base, ProjectionElem::Deref)) => {
`
``
974
`+
// FIXME: why do statics and raw pointers get excluded here? This makes
`
``
975
`+
// some code involving mutable pointers unstable, but it is unclear
`
``
976
`+
// why that code is treated differently from mutable references.
`
``
977
`+
// Once TransientMutBorrow and TransientCellBorrow are stable,
`
``
978
`+
// this can probably be cleaned up without any behavioral changes.
`
``
979
+
951
980
`` // A borrow of a static
also looks like &(*_1)
in the MIR, but _1
is a const
``
952
981
`// that points to the allocation for the static. Don't treat these as reborrows.
`
953
982
`if body.local_decls[place_base.local].is_ref_to_static() {
`