Skip to content

Commit c35035c

Browse files
committed
Auto merge of rust-lang#97025 - ouz-a:mini-derefer-generator, r=davidtwco
Add validation layer for Derefer _Follow up work to rust-lang#96549 rust-lang#96116 rust-lang#95857 #95649_ This adds validation for Derefer making sure it is always the first projection. r? rust-lang/mir-opt
2 parents 4a8d2e3 + e71913e commit c35035c

File tree

39 files changed

+299
-59
lines changed

39 files changed

+299
-59
lines changed

compiler/rustc_const_eval/src/transform/validate.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rustc_data_structures::fx::FxHashSet;
44
use rustc_index::bit_set::BitSet;
55
use rustc_infer::infer::TyCtxtInferExt;
66
use rustc_middle::mir::interpret::Scalar;
7+
use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
78
use rustc_middle::mir::visit::{PlaceContext, Visitor};
89
use rustc_middle::mir::{
910
traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, Local, Location, MirPass,
@@ -302,9 +303,17 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
302303
self.super_projection_elem(local, proj_base, elem, context, location);
303304
}
304305

305-
fn visit_place(&mut self, place: &Place<'tcx>, _: PlaceContext, _: Location) {
306+
fn visit_place(&mut self, place: &Place<'tcx>, cntxt: PlaceContext, location: Location) {
306307
// Set off any `bug!`s in the type computation code
307308
let _ = place.ty(&self.body.local_decls, self.tcx);
309+
310+
if self.mir_phase >= MirPhase::Derefered
311+
&& place.projection.len() > 1
312+
&& cntxt != PlaceContext::NonUse(VarDebugInfo)
313+
&& place.projection[1..].contains(&ProjectionElem::Deref)
314+
{
315+
self.fail(location, format!("{:?}, has deref at the wrong place", place));
316+
}
308317
}
309318

310319
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {

compiler/rustc_middle/src/mir/mod.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,14 @@ pub enum MirPhase {
172172
/// terminator means that the auto-generated drop glue will be invoked. Also, `Copy` operands
173173
/// are allowed for non-`Copy` types.
174174
DropsLowered = 3,
175+
/// After this projections may only contain deref projections as the first element.
176+
Derefered = 4,
175177
/// Beginning with this phase, the following variant is disallowed:
176178
/// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array`
177179
///
178180
/// And the following variant is allowed:
179181
/// * [`StatementKind::SetDiscriminant`]
180-
Deaggregated = 4,
182+
Deaggregated = 5,
181183
/// Before this phase, generators are in the "source code" form, featuring `yield` statements
182184
/// and such. With this phase change, they are transformed into a proper state machine. Running
183185
/// optimizations before this change can be potentially dangerous because the source code is to
@@ -191,8 +193,8 @@ pub enum MirPhase {
191193
/// Beginning with this phase, the following variants are disallowed:
192194
/// * [`TerminatorKind::Yield`](terminator::TerminatorKind::Yield)
193195
/// * [`TerminatorKind::GeneratorDrop`](terminator::TerminatorKind::GeneratorDrop)
194-
GeneratorsLowered = 5,
195-
Optimized = 6,
196+
GeneratorsLowered = 6,
197+
Optimized = 7,
196198
}
197199

198200
impl MirPhase {
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use crate::MirPass;
22
use rustc_index::vec::IndexVec;
33
use rustc_middle::mir::patch::MirPatch;
4+
use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
45
use rustc_middle::mir::visit::{MutVisitor, PlaceContext};
56
use rustc_middle::mir::*;
67
use rustc_middle::ty::TyCtxt;
8+
79
pub struct Derefer;
810

911
pub struct DerefChecker<'tcx> {
@@ -17,63 +19,68 @@ impl<'tcx> MutVisitor<'tcx> for DerefChecker<'tcx> {
1719
self.tcx
1820
}
1921

20-
fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, loc: Location) {
21-
let mut place_local = place.local;
22-
let mut last_len = 0;
23-
let mut last_deref_idx = 0;
22+
fn visit_place(&mut self, place: &mut Place<'tcx>, cntxt: PlaceContext, loc: Location) {
23+
if !place.projection.is_empty()
24+
&& cntxt != PlaceContext::NonUse(VarDebugInfo)
25+
&& place.projection[1..].contains(&ProjectionElem::Deref)
26+
{
27+
let mut place_local = place.local;
28+
let mut last_len = 0;
29+
let mut last_deref_idx = 0;
2430

25-
let mut prev_temp: Option<Local> = None;
31+
let mut prev_temp: Option<Local> = None;
2632

27-
for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
28-
if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
29-
last_deref_idx = idx;
30-
}
31-
}
32-
33-
for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
34-
if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
35-
let ty = p_ref.ty(&self.local_decls, self.tcx).ty;
36-
let temp = self.patcher.new_local_with_info(
37-
ty,
38-
self.local_decls[p_ref.local].source_info.span,
39-
Some(Box::new(LocalInfo::DerefTemp)),
40-
);
41-
42-
self.patcher.add_statement(loc, StatementKind::StorageLive(temp));
43-
44-
// We are adding current p_ref's projections to our
45-
// temp value, excluding projections we already covered.
46-
let deref_place = Place::from(place_local)
47-
.project_deeper(&p_ref.projection[last_len..], self.tcx);
48-
49-
self.patcher.add_assign(
50-
loc,
51-
Place::from(temp),
52-
Rvalue::Use(Operand::Move(deref_place)),
53-
);
54-
place_local = temp;
55-
last_len = p_ref.projection.len();
56-
57-
// Change `Place` only if we are actually at the Place's last deref
58-
if idx == last_deref_idx {
59-
let temp_place =
60-
Place::from(temp).project_deeper(&place.projection[idx..], self.tcx);
61-
*place = temp_place;
33+
for (idx, elem) in place.projection[0..].iter().enumerate() {
34+
if *elem == ProjectionElem::Deref {
35+
last_deref_idx = idx;
6236
}
63-
64-
// We are destroying the previous temp since it's no longer used.
65-
if let Some(prev_temp) = prev_temp {
66-
self.patcher.add_statement(loc, StatementKind::StorageDead(prev_temp));
37+
}
38+
for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
39+
if !p_ref.projection.is_empty() && p_elem == ProjectionElem::Deref {
40+
let ty = p_ref.ty(&self.local_decls, self.tcx).ty;
41+
let temp = self.patcher.new_local_with_info(
42+
ty,
43+
self.local_decls[p_ref.local].source_info.span,
44+
Some(Box::new(LocalInfo::DerefTemp)),
45+
);
46+
47+
self.patcher.add_statement(loc, StatementKind::StorageLive(temp));
48+
49+
// We are adding current p_ref's projections to our
50+
// temp value, excluding projections we already covered.
51+
let deref_place = Place::from(place_local)
52+
.project_deeper(&p_ref.projection[last_len..], self.tcx);
53+
54+
self.patcher.add_assign(
55+
loc,
56+
Place::from(temp),
57+
Rvalue::Use(Operand::Move(deref_place)),
58+
);
59+
place_local = temp;
60+
last_len = p_ref.projection.len();
61+
62+
// Change `Place` only if we are actually at the Place's last deref
63+
if idx == last_deref_idx {
64+
let temp_place =
65+
Place::from(temp).project_deeper(&place.projection[idx..], self.tcx);
66+
*place = temp_place;
67+
}
68+
69+
// We are destroying the previous temp since it's no longer used.
70+
if let Some(prev_temp) = prev_temp {
71+
self.patcher.add_statement(loc, StatementKind::StorageDead(prev_temp));
72+
}
73+
74+
prev_temp = Some(temp);
6775
}
68-
69-
prev_temp = Some(temp);
7076
}
71-
}
7277

73-
// Since we won't be able to reach final temp, we destroy it outside the loop.
74-
if let Some(prev_temp) = prev_temp {
75-
let last_loc = Location { block: loc.block, statement_index: loc.statement_index + 1 };
76-
self.patcher.add_statement(last_loc, StatementKind::StorageDead(prev_temp));
78+
// Since we won't be able to reach final temp, we destroy it outside the loop.
79+
if let Some(prev_temp) = prev_temp {
80+
let last_loc =
81+
Location { block: loc.block, statement_index: loc.statement_index + 1 };
82+
self.patcher.add_statement(last_loc, StatementKind::StorageDead(prev_temp));
83+
}
7784
}
7885
}
7986
}
@@ -92,5 +99,6 @@ pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
9299
impl<'tcx> MirPass<'tcx> for Derefer {
93100
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
94101
deref_finder(tcx, body);
102+
body.phase = MirPhase::Derefered;
95103
}
96104
}

compiler/rustc_mir_transform/src/generator.rs

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
//! For generators with state 1 (returned) and state 2 (poisoned) it does nothing.
5050
//! Otherwise it drops all the values in scope at the last suspension point.
5151
52+
use crate::deref_separator::deref_finder;
5253
use crate::simplify;
5354
use crate::util::expand_aggregate;
5455
use crate::MirPass;
@@ -1368,6 +1369,9 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
13681369

13691370
// Create the Generator::resume function
13701371
create_generator_resume_function(tcx, transform, body, can_return);
1372+
1373+
// Run derefer to fix Derefs that are not in the first place
1374+
deref_finder(tcx, body);
13711375
}
13721376
}
13731377

compiler/rustc_mir_transform/src/inline.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! Inlining pass for MIR functions
2-
2+
use crate::deref_separator::deref_finder;
33
use rustc_attr::InlineAttr;
44
use rustc_index::bit_set::BitSet;
55
use rustc_index::vec::Idx;
@@ -53,6 +53,7 @@ impl<'tcx> MirPass<'tcx> for Inline {
5353
debug!("running simplify cfg on {:?}", body.source);
5454
CfgSimplifier::new(body).simplify();
5555
remove_dead_blocks(tcx, body);
56+
deref_finder(tcx, body);
5657
}
5758
}
5859
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
- // MIR for `main` before Derefer
2+
+ // MIR for `main` after Derefer
3+
4+
fn main() -> () {
5+
let mut _0: (); // return place in scope 0 at $DIR/derefer_inline_test.rs:9:11: 9:11
6+
let _1: std::boxed::Box<std::boxed::Box<u32>>; // in scope 0 at $DIR/derefer_inline_test.rs:10:5: 10:12
7+
let mut _2: usize; // in scope 0 at $DIR/derefer_inline_test.rs:10:5: 10:12
8+
let mut _3: usize; // in scope 0 at $DIR/derefer_inline_test.rs:10:5: 10:12
9+
let mut _4: *mut u8; // in scope 0 at $DIR/derefer_inline_test.rs:10:5: 10:12
10+
let mut _5: std::boxed::Box<std::boxed::Box<u32>>; // in scope 0 at $DIR/derefer_inline_test.rs:10:5: 10:12
11+
let mut _6: (); // in scope 0 at $DIR/derefer_inline_test.rs:10:11: 10:12
12+
scope 1 {
13+
}
14+
15+
bb0: {
16+
StorageLive(_1); // scope 0 at $DIR/derefer_inline_test.rs:10:5: 10:12
17+
_2 = SizeOf(std::boxed::Box<u32>); // scope 1 at $DIR/derefer_inline_test.rs:10:5: 10:12
18+
_3 = AlignOf(std::boxed::Box<u32>); // scope 1 at $DIR/derefer_inline_test.rs:10:5: 10:12
19+
_4 = alloc::alloc::exchange_malloc(move _2, move _3) -> bb1; // scope 1 at $DIR/derefer_inline_test.rs:10:5: 10:12
20+
// mir::Constant
21+
// + span: $DIR/derefer_inline_test.rs:10:5: 10:12
22+
// + literal: Const { ty: unsafe fn(usize, usize) -> *mut u8 {alloc::alloc::exchange_malloc}, val: Value(Scalar(<ZST>)) }
23+
}
24+
25+
bb1: {
26+
StorageLive(_5); // scope 0 at $DIR/derefer_inline_test.rs:10:5: 10:12
27+
_5 = ShallowInitBox(move _4, std::boxed::Box<u32>); // scope 0 at $DIR/derefer_inline_test.rs:10:5: 10:12
28+
(*_5) = f() -> [return: bb2, unwind: bb5]; // scope 0 at $DIR/derefer_inline_test.rs:10:9: 10:12
29+
// mir::Constant
30+
// + span: $DIR/derefer_inline_test.rs:10:9: 10:10
31+
// + literal: Const { ty: fn() -> Box<u32> {f}, val: Value(Scalar(<ZST>)) }
32+
}
33+
34+
bb2: {
35+
_1 = move _5; // scope 0 at $DIR/derefer_inline_test.rs:10:5: 10:12
36+
goto -> bb3; // scope 0 at $DIR/derefer_inline_test.rs:10:11: 10:12
37+
}
38+
39+
bb3: {
40+
StorageDead(_5); // scope 0 at $DIR/derefer_inline_test.rs:10:11: 10:12
41+
drop(_1) -> [return: bb4, unwind: bb6]; // scope 0 at $DIR/derefer_inline_test.rs:10:12: 10:13
42+
}
43+
44+
bb4: {
45+
StorageDead(_1); // scope 0 at $DIR/derefer_inline_test.rs:10:12: 10:13
46+
_0 = const (); // scope 0 at $DIR/derefer_inline_test.rs:9:11: 11:2
47+
return; // scope 0 at $DIR/derefer_inline_test.rs:11:2: 11:2
48+
}
49+
50+
bb5 (cleanup): {
51+
goto -> bb8; // scope 0 at $DIR/derefer_inline_test.rs:10:11: 10:12
52+
}
53+
54+
bb6 (cleanup): {
55+
resume; // scope 0 at $DIR/derefer_inline_test.rs:9:1: 11:2
56+
}
57+
58+
bb7 (cleanup): {
59+
_6 = alloc::alloc::box_free::<Box<u32>, std::alloc::Global>(move (_5.0: std::ptr::Unique<std::boxed::Box<u32>>), move (_5.1: std::alloc::Global)) -> bb6; // scope 0 at $DIR/derefer_inline_test.rs:10:11: 10:12
60+
// mir::Constant
61+
// + span: $DIR/derefer_inline_test.rs:10:11: 10:12
62+
// + literal: Const { ty: unsafe fn(Unique<Box<u32>>, std::alloc::Global) {alloc::alloc::box_free::<Box<u32>, std::alloc::Global>}, val: Value(Scalar(<ZST>)) }
63+
}
64+
65+
bb8 (cleanup): {
66+
goto -> bb7; // scope 0 at $DIR/derefer_inline_test.rs:10:11: 10:12
67+
}
68+
}
69+
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// EMIT_MIR derefer_inline_test.main.Derefer.diff
2+
// ignore-wasm32 compiled with panic=abort by default
3+
4+
#![feature(box_syntax)]
5+
#[inline]
6+
fn f() -> Box<u32> {
7+
box 0
8+
}
9+
fn main() {
10+
box f();
11+
}

src/test/mir-opt/inline/dyn_trait.get_query.Inline.diff

+4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@
5757
StorageDead(_4); // scope 1 at $DIR/dyn-trait.rs:34:24: 34:25
5858
StorageDead(_2); // scope 0 at $DIR/dyn-trait.rs:35:1: 35:2
5959
return; // scope 0 at $DIR/dyn-trait.rs:35:2: 35:2
60+
+ }
61+
+
62+
+ bb3 (cleanup): {
63+
+ resume; // scope 0 at $DIR/dyn-trait.rs:32:1: 35:2
6064
}
6165
}
6266

src/test/mir-opt/inline/dyn_trait.try_execute_query.Inline.diff

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
+ StorageDead(_4); // scope 1 at $DIR/dyn-trait.rs:21:21: 21:22
3333
StorageDead(_2); // scope 0 at $DIR/dyn-trait.rs:27:15: 27:16
3434
return; // scope 0 at $DIR/dyn-trait.rs:28:2: 28:2
35+
+ }
36+
+
37+
+ bb2 (cleanup): {
38+
+ resume; // scope 0 at $DIR/dyn-trait.rs:26:1: 28:2
3539
}
3640
}
3741

src/test/mir-opt/inline/inline_any_operand.bar.Inline.after.mir

+4
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,8 @@ fn bar() -> bool {
4141
StorageDead(_1); // scope 0 at $DIR/inline-any-operand.rs:13:1: 13:2
4242
return; // scope 0 at $DIR/inline-any-operand.rs:13:2: 13:2
4343
}
44+
45+
bb1 (cleanup): {
46+
resume; // scope 0 at $DIR/inline-any-operand.rs:10:1: 13:2
47+
}
4448
}

src/test/mir-opt/inline/inline_closure.foo.Inline.after.mir

+4
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,8 @@ fn foo(_1: T, _2: i32) -> i32 {
4646
StorageDead(_3); // scope 0 at $DIR/inline-closure.rs:13:1: 13:2
4747
return; // scope 0 at $DIR/inline-closure.rs:13:2: 13:2
4848
}
49+
50+
bb1 (cleanup): {
51+
resume; // scope 0 at $DIR/inline-closure.rs:10:1: 13:2
52+
}
4953
}

src/test/mir-opt/inline/inline_closure_borrows_arg.foo.Inline.after.mir

+4
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,8 @@ fn foo(_1: T, _2: &i32) -> i32 {
5353
StorageDead(_3); // scope 0 at $DIR/inline-closure-borrows-arg.rs:17:1: 17:2
5454
return; // scope 0 at $DIR/inline-closure-borrows-arg.rs:17:2: 17:2
5555
}
56+
57+
bb1 (cleanup): {
58+
resume; // scope 0 at $DIR/inline-closure-borrows-arg.rs:11:1: 17:2
59+
}
5660
}

src/test/mir-opt/inline/inline_closure_captures.foo.Inline.after.mir

+4
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,8 @@ fn foo(_1: T, _2: i32) -> (i32, T) {
6666
StorageDead(_3); // scope 0 at $DIR/inline-closure-captures.rs:13:1: 13:2
6767
return; // scope 0 at $DIR/inline-closure-captures.rs:13:2: 13:2
6868
}
69+
70+
bb1 (cleanup): {
71+
resume; // scope 0 at $DIR/inline-closure-captures.rs:10:1: 13:2
72+
}
6973
}

src/test/mir-opt/inline/inline_compatibility.inlined_no_sanitize.Inline.diff

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:24:18: 24:19
2020
_0 = const (); // scope 0 at $DIR/inline-compatibility.rs:23:37: 25:2
2121
return; // scope 0 at $DIR/inline-compatibility.rs:25:2: 25:2
22+
+ }
23+
+
24+
+ bb1 (cleanup): {
25+
+ resume; // scope 0 at $DIR/inline-compatibility.rs:23:1: 25:2
2226
}
2327
}
2428

src/test/mir-opt/inline/inline_compatibility.inlined_target_feature.Inline.diff

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
StorageDead(_1); // scope 0 at $DIR/inline-compatibility.rs:13:21: 13:22
2020
_0 = const (); // scope 0 at $DIR/inline-compatibility.rs:12:40: 14:2
2121
return; // scope 0 at $DIR/inline-compatibility.rs:14:2: 14:2
22+
+ }
23+
+
24+
+ bb1 (cleanup): {
25+
+ resume; // scope 0 at $DIR/inline-compatibility.rs:12:1: 14:2
2226
}
2327
}
2428

src/test/mir-opt/inline/inline_cycle.one.Inline.diff

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
StorageDead(_1); // scope 0 at $DIR/inline-cycle.rs:14:24: 14:25
2323
_0 = const (); // scope 0 at $DIR/inline-cycle.rs:13:10: 15:2
2424
return; // scope 0 at $DIR/inline-cycle.rs:15:2: 15:2
25+
+ }
26+
+
27+
+ bb2 (cleanup): {
28+
+ resume; // scope 0 at $DIR/inline-cycle.rs:13:1: 15:2
2529
}
2630
}
2731

0 commit comments

Comments
 (0)