Skip to content

Commit 20de910

Browse files
committed
Auto merge of #154341 - RalfJung:retag-on-typed-copy, r=oli-obk
Make retags an implicit part of typed copies Ever since Stacked Borrows was first implemented in Miri, that was done with `Retag` statements: given a place (usually a local variable), those statements find all references stored inside the place and refresh their tags to ensure the aliasing requirements are upheld. However, this is a somewhat unsatisfying approach for multiple reasons: - It leaves open the [question](rust-lang/unsafe-code-guidelines#371) of where to even put `Retag` statements. Over time, the AddRetag pass settled on one possible answer to this, but it wasn't very canonical. - For assignments of the form `*ptr = expr`, if the assignment involves copying a reference, we probably want to do a retag -- but if we do a `Retag(*ptr)` as the next instruction, it can be non-trivial to argue that this even retags the right value, so we refrained from doing retags in that case. This has [come up](llvm/llvm-project#160913 (comment)) as a potential issue for Rust making better use of LLVM "captures" annotations. (That said, there might be [other ways](rust-lang/unsafe-code-guidelines#593 (comment)) to obtain this desired optimization.) - Normal compilation avoids generating retags, but we still generate LLVM IR with `noalias`. What does that even mean? How do MIR optimization passes interact with retags? These are questions we have to figure out to make better use of aliasing information, but currently we can't even really ask such questions. I think we should resolve all that by making retags part of what happens during a typed copy (a concept and interpreter infrastructure that did not exist yet when retags were initially introduced). Under this proposal, when executing a MIR assignment statement, what conceptually happens is as follows: - We evaluate the LHS to a place. - We evaluate the RHS to a value. This does a typed load from memory if needed, raising UB if memory does not contain a valid representation of the assignment's type. - We walk that value, identify all references inside of it, and retag them. If this happens as part of passing a function argument, this is a protecting retag. - We store (a representation of) the value into the place. However, this semantics doesn't fully work: there's a mandatory MIR pass that turns expressions like `&mut ***ptr` into intermediate deref's. Those must *not* do any retags. So far this happened because the AddRetag pass did not add retags for assignments to deref temporaries, but that information is not recorded in cross-crate MIR. Therefore I instead added a field to `Rvalue::Use` to indicate whether this value should be retagged or not. A non-retagging copy seems like a sufficiently canonical primitive that we should be able to express it. Dealing with the fallout from that is a large chunk of the overall diff. (I also considered adding this field to `StatementKind::Assign` instead, but decided against that as we only actually need it for `Rvalue::Use`. I am not sure if this was the right call...) This neatly answers the question of when retags should occur, and handles cases like `*ptr = expr`. It avoids traversing values twice in Miri. It makes codegen's use of `noalias` sound wrt the actual MIR that it is working on. It also gives us a target semantics to evaluate MIR opts against. However, I did not carefully check all MIR opts -- in particular, GVN needs a thorough look under the new semantics; it currently can turn alias-correct code into alias-incorrect code. (But this PR doesn't make things any worse for normal compilation where the retag indicator is anyway ignored.) Another side-effect of this PR is that `-Zmiri-disable-validation` now also disables alias checking. It'd be nicer to keep them orthogonal but I find this an acceptable price to pay. - [rustc benchmark results](#154341 (comment)) - [miri benchmark results](#154341 (comment))
2 parents 3d5dfdc + e402c1e commit 20de910

240 files changed

Lines changed: 1161 additions & 2520 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compiler/rustc_borrowck/src/dataflow.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,6 @@ impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
578578
mir::StatementKind::FakeRead(..)
579579
| mir::StatementKind::SetDiscriminant { .. }
580580
| mir::StatementKind::StorageLive(..)
581-
| mir::StatementKind::Retag { .. }
582581
| mir::StatementKind::PlaceMention(..)
583582
| mir::StatementKind::AscribeUserType(..)
584583
| mir::StatementKind::Coverage(..)

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4325,7 +4325,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
43254325
// Otherwise, look at other types of assignment.
43264326
let assigned_from = match rvalue {
43274327
Rvalue::Ref(_, _, assigned_from) => assigned_from,
4328-
Rvalue::Use(operand) => match operand {
4328+
Rvalue::Use(operand, _) => match operand {
43294329
Operand::Copy(assigned_from) | Operand::Move(assigned_from) => {
43304330
assigned_from
43314331
}

compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -875,7 +875,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
875875
match rvalue {
876876
// If we see a use, we should check whether it is our data, and if so
877877
// update the place that we're looking for to that new place.
878-
Rvalue::Use(operand) => match operand {
878+
Rvalue::Use(operand, _) => match operand {
879879
Operand::Copy(place) | Operand::Move(place) => {
880880
if let Some(from) = place.as_local() {
881881
if from == target {

compiler/rustc_borrowck/src/diagnostics/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
243243
let mut target = place.local_or_deref_local();
244244
for stmt in &self.body[location.block].statements[location.statement_index..] {
245245
debug!("add_moved_or_invoked_closure_note: stmt={:?} target={:?}", stmt, target);
246-
if let StatementKind::Assign(box (into, Rvalue::Use(from))) = &stmt.kind {
246+
if let StatementKind::Assign(box (into, Rvalue::Use(from, _))) = &stmt.kind {
247247
debug!("add_fnonce_closure_note: into={:?} from={:?}", into, from);
248248
match from {
249249
Operand::Copy(place) | Operand::Move(place)

compiler/rustc_borrowck/src/diagnostics/move_errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
122122
// to a user variable is when initializing it.
123123
// If that ever stops being the case, then the ever initialized
124124
// flow could be used.
125-
if let Some(StatementKind::Assign(box (place, Rvalue::Use(Operand::Move(move_from))))) =
125+
if let Some(StatementKind::Assign(box (place, Rvalue::Use(Operand::Move(move_from), _)))) =
126126
self.body.basic_blocks[location.block]
127127
.statements
128128
.get(location.statement_index)

compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1309,7 +1309,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
13091309
if let Some(mir::Statement {
13101310
source_info: _,
13111311
kind:
1312-
mir::StatementKind::Assign(box (_, mir::Rvalue::Use(mir::Operand::Copy(place)))),
1312+
mir::StatementKind::Assign(box (
1313+
_,
1314+
mir::Rvalue::Use(mir::Operand::Copy(place), _),
1315+
)),
13131316
..
13141317
}) = first_assignment_stmt
13151318
{

compiler/rustc_borrowck/src/lib.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,6 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a,
853853
);
854854
}
855855
StatementKind::Nop
856-
| StatementKind::Retag { .. }
857856
| StatementKind::SetDiscriminant { .. } => {
858857
bug!("Statement not allowed in this MIR phase")
859858
}
@@ -1540,7 +1539,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
15401539

15411540
Rvalue::ThreadLocalRef(_) => {}
15421541

1543-
Rvalue::Use(operand)
1542+
Rvalue::Use(operand, _)
15441543
| Rvalue::Repeat(operand, _)
15451544
| Rvalue::UnaryOp(_ /*un_op*/, operand)
15461545
| Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/) => {
@@ -1693,7 +1692,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
16931692
StatementKind::Assign(box (
16941693
_,
16951694
Rvalue::Ref(_, _, source)
1696-
| Rvalue::Use(Operand::Copy(source) | Operand::Move(source)),
1695+
| Rvalue::Use(Operand::Copy(source) | Operand::Move(source), _),
16971696
)) => {
16981697
propagate_closure_used_mut_place(self, source);
16991698
}

compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> {
8383
}
8484
StatementKind::ConstEvalCounter
8585
| StatementKind::Nop
86-
| StatementKind::Retag { .. }
8786
| StatementKind::BackwardIncompatibleDropHint { .. }
8887
| StatementKind::SetDiscriminant { .. } => {
8988
bug!("Statement not allowed in this MIR phase")
@@ -294,7 +293,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
294293

295294
Rvalue::ThreadLocalRef(_) => {}
296295

297-
Rvalue::Use(operand)
296+
Rvalue::Use(operand, _)
298297
| Rvalue::Repeat(operand, _)
299298
| Rvalue::UnaryOp(_ /*un_op*/, operand)
300299
| Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/) => {

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
697697
| StatementKind::FakeRead(..)
698698
| StatementKind::StorageLive(..)
699699
| StatementKind::StorageDead(..)
700-
| StatementKind::Retag { .. }
701700
| StatementKind::Coverage(..)
702701
| StatementKind::ConstEvalCounter
703702
| StatementKind::PlaceMention(..)
@@ -1652,7 +1651,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
16521651
.unwrap();
16531652
}
16541653

1655-
Rvalue::Use(_)
1654+
Rvalue::Use(_, _)
16561655
| Rvalue::UnaryOp(_, _)
16571656
| Rvalue::CopyForDeref(_)
16581657
| Rvalue::BinaryOp(..)
@@ -2215,8 +2214,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
22152214
/// rvalue and will be unified with the inferred type.
22162215
fn rvalue_user_ty(&self, rvalue: &Rvalue<'tcx>) -> Option<UserTypeAnnotationIndex> {
22172216
match rvalue {
2218-
Rvalue::Use(_)
2219-
| Rvalue::ThreadLocalRef(_)
2217+
Rvalue::Use(..)
2218+
| Rvalue::ThreadLocalRef(..)
22202219
| Rvalue::Repeat(..)
22212220
| Rvalue::Ref(..)
22222221
| Rvalue::RawPtr(..)

compiler/rustc_codegen_cranelift/src/base.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
620620
let lval = codegen_place(fx, to_place_and_rval.0);
621621
let dest_layout = lval.layout();
622622
match to_place_and_rval.1 {
623-
Rvalue::Use(ref operand) => {
623+
Rvalue::Use(ref operand, _) => {
624624
let val = codegen_operand(fx, operand);
625625
lval.write_cvalue(fx, val);
626626
}
@@ -909,7 +909,6 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt:
909909
| StatementKind::ConstEvalCounter
910910
| StatementKind::Nop
911911
| StatementKind::FakeRead(..)
912-
| StatementKind::Retag { .. }
913912
| StatementKind::PlaceMention(..)
914913
| StatementKind::BackwardIncompatibleDropHint { .. }
915914
| StatementKind::AscribeUserType(..) => {}

0 commit comments

Comments
 (0)