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() {

`