Skip to content

Commit 876f04d

Browse files
refine scopes
Co-authored-by: Felix S <[email protected]>
1 parent 9cdefd7 commit 876f04d

File tree

7 files changed

+199
-22
lines changed

7 files changed

+199
-22
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4134,6 +4134,7 @@ dependencies = [
41344134
"rustc_session",
41354135
"rustc_span",
41364136
"rustc_target",
4137+
"smallvec",
41374138
"tracing",
41384139
]
41394140

compiler/rustc_middle/src/middle/region.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,8 @@ pub struct ScopeTree {
234234
/// escape into 'static and should have no local cleanup scope.
235235
rvalue_scopes: FxHashMap<hir::ItemLocalId, Option<Scope>>,
236236

237+
eager_scopes: FxHashMap<hir::ItemLocalId, Scope>,
238+
237239
/// If there are any `yield` nested within a scope, this map
238240
/// stores the `Span` of the last one and its index in the
239241
/// postorder of the Visitor traversal on the HIR.
@@ -358,6 +360,32 @@ impl ScopeTree {
358360
self.rvalue_scopes.insert(var, lifetime);
359361
}
360362

363+
pub fn record_local_access_scope(&mut self, var: hir::ItemLocalId, proposed_lifetime: Scope) {
364+
debug!("record_local_access_scope(sub={:?}, sup={:?})", var, proposed_lifetime);
365+
let mut id = Scope { id: var, data: ScopeData::Node };
366+
367+
while let Some(&(p, _)) = self.parent_map.get(&id) {
368+
match p.data {
369+
ScopeData::Destruction => return,
370+
_ => id = p,
371+
}
372+
if id == proposed_lifetime {
373+
// proposed lifetime outlives the destruction lifetime
374+
self.record_rvalue_scope(var, Some(proposed_lifetime));
375+
return;
376+
}
377+
}
378+
}
379+
380+
pub fn record_eager_scope(&mut self, var: hir::ItemLocalId, proposed_lifetime: Scope) {
381+
if let Some(destruction) = self.temporary_scope(var) {
382+
if self.is_subscope_of(destruction, proposed_lifetime) {
383+
return;
384+
}
385+
}
386+
self.eager_scopes.insert(var, proposed_lifetime);
387+
}
388+
361389
/// Returns the narrowest scope that encloses `id`, if any.
362390
pub fn opt_encl_scope(&self, id: Scope) -> Option<Scope> {
363391
self.parent_map.get(&id).cloned().map(|(p, _)| p)
@@ -393,6 +421,9 @@ impl ScopeTree {
393421
}
394422
_ => id = p,
395423
}
424+
if let Some(&eager_scope) = self.eager_scopes.get(&id.item_local_id()) {
425+
return Some(eager_scope);
426+
}
396427
}
397428

398429
debug!("temporary_scope({:?}) = None", expr_id);
@@ -444,6 +475,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for ScopeTree {
444475
ref var_map,
445476
ref destruction_scopes,
446477
ref rvalue_scopes,
478+
ref eager_scopes,
447479
ref yield_in_scope,
448480
} = *self;
449481

@@ -456,6 +488,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for ScopeTree {
456488
var_map.hash_stable(hcx, hasher);
457489
destruction_scopes.hash_stable(hcx, hasher);
458490
rvalue_scopes.hash_stable(hcx, hasher);
491+
eager_scopes.hash_stable(hcx, hasher);
459492
yield_in_scope.hash_stable(hcx, hasher);
460493
}
461494
}

compiler/rustc_passes/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
tracing = "0.1"
8+
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
89
rustc_middle = { path = "../rustc_middle" }
910
rustc_attr = { path = "../rustc_attr" }
1011
rustc_data_structures = { path = "../rustc_data_structures" }

compiler/rustc_passes/src/region.rs

Lines changed: 150 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use rustc_middle::ty::query::Providers;
1818
use rustc_middle::ty::TyCtxt;
1919
use rustc_span::source_map;
2020
use rustc_span::Span;
21+
use smallvec::SmallVec;
2122

2223
use std::mem;
2324

@@ -203,6 +204,106 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h
203204
visitor.cx.parent = prev_parent;
204205
}
205206

207+
fn mark_local_terminating_scopes<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> FxHashSet<hir::ItemLocalId> {
208+
struct LocalAccessResolutionVisitor<'a> {
209+
locals: &'a mut FxHashSet<hir::ItemLocalId>,
210+
}
211+
impl<'a> LocalAccessResolutionVisitor<'a> {
212+
fn probe<'b>(&mut self, expr: &'b Expr<'b>) {
213+
if self.locals.contains(&expr.hir_id.local_id) {
214+
return;
215+
}
216+
let mut nested_expr = expr;
217+
let mut ops = SmallVec::<[_; 4]>::new();
218+
enum OpTy {
219+
Ref,
220+
Deref,
221+
Project,
222+
Local,
223+
}
224+
loop {
225+
match nested_expr.kind {
226+
hir::ExprKind::Path(hir::QPath::Resolved(
227+
_,
228+
hir::Path { res: hir::def::Res::Local(_), .. },
229+
)) => {
230+
ops.push((nested_expr, OpTy::Local));
231+
break;
232+
}
233+
hir::ExprKind::AddrOf(_, _, subexpr) => {
234+
ops.push((nested_expr, OpTy::Ref));
235+
nested_expr = subexpr;
236+
}
237+
hir::ExprKind::Unary(hir::UnOp::Deref, subexpr) => {
238+
ops.push((nested_expr, OpTy::Deref));
239+
nested_expr = subexpr;
240+
}
241+
hir::ExprKind::Field(subexpr, _) => {
242+
ops.push((nested_expr, OpTy::Project));
243+
nested_expr = subexpr;
244+
}
245+
hir::ExprKind::Index(subexpr, idxexpr) => {
246+
ops.push((nested_expr, OpTy::Project));
247+
nested_expr = subexpr;
248+
intravisit::walk_expr(self, idxexpr);
249+
}
250+
_ => {
251+
drop(ops);
252+
intravisit::walk_expr(self, expr);
253+
return;
254+
}
255+
}
256+
}
257+
let mut ref_level = SmallVec::<[_; 4]>::new();
258+
let mut ops_iter = ops.into_iter().rev();
259+
ops_iter.next().unwrap();
260+
for (expr, op) in ops_iter {
261+
match op {
262+
OpTy::Ref => {
263+
ref_level.push(expr);
264+
}
265+
OpTy::Deref => {
266+
if let Some(ref_expr) = ref_level.pop() {
267+
self.locals.insert(ref_expr.hir_id.local_id);
268+
}
269+
}
270+
OpTy::Project => {
271+
ref_level.clear();
272+
}
273+
OpTy::Local => {
274+
panic!("unexpected encounter of Local")
275+
}
276+
}
277+
}
278+
self.locals.insert(expr.hir_id.local_id);
279+
}
280+
}
281+
impl<'a, 'b> Visitor<'a> for LocalAccessResolutionVisitor<'b> {
282+
type Map = intravisit::ErasedMap<'a>;
283+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
284+
NestedVisitorMap::None
285+
}
286+
fn visit_expr(&mut self, expr: &'a Expr<'a>) {
287+
match expr.kind {
288+
hir::ExprKind::AddrOf(..)
289+
| hir::ExprKind::Unary(hir::UnOp::Deref, _)
290+
| hir::ExprKind::Field(..)
291+
| hir::ExprKind::Index(..)
292+
| hir::ExprKind::Path(..) => self.probe(expr),
293+
hir::ExprKind::Block(..)
294+
| hir::ExprKind::Closure(..)
295+
| hir::ExprKind::ConstBlock(..) => {}
296+
_ => intravisit::walk_expr(self, expr),
297+
}
298+
}
299+
}
300+
let mut locals = Default::default();
301+
let mut resolution_visitor = LocalAccessResolutionVisitor { locals: &mut locals };
302+
resolution_visitor.visit_expr(expr);
303+
// visitor.terminating_scopes.extend(locals.iter().copied());
304+
locals
305+
}
306+
206307
fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
207308
debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr);
208309

@@ -396,7 +497,15 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
396497
let expr_cx = visitor.cx;
397498
visitor.enter_scope(Scope { id: then.hir_id.local_id, data: ScopeData::IfThen });
398499
visitor.cx.var_parent = visitor.cx.parent;
399-
visitor.visit_expr(cond);
500+
{
501+
visitor.visit_expr(cond);
502+
let lifetime = Scope { id: cond.hir_id.local_id, data: ScopeData::Node };
503+
for local in mark_local_terminating_scopes(cond) {
504+
if local != cond.hir_id.local_id {
505+
visitor.scope_tree.record_local_access_scope(local, lifetime);
506+
}
507+
}
508+
}
400509
visitor.visit_expr(then);
401510
visitor.cx = expr_cx;
402511
visitor.visit_expr(otherwise);
@@ -406,11 +515,39 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
406515
let expr_cx = visitor.cx;
407516
visitor.enter_scope(Scope { id: then.hir_id.local_id, data: ScopeData::IfThen });
408517
visitor.cx.var_parent = visitor.cx.parent;
409-
visitor.visit_expr(cond);
518+
{
519+
visitor.visit_expr(cond);
520+
let lifetime = Scope { id: cond.hir_id.local_id, data: ScopeData::Node };
521+
for local in mark_local_terminating_scopes(cond) {
522+
if local != cond.hir_id.local_id {
523+
visitor.scope_tree.record_local_access_scope(local, lifetime);
524+
}
525+
}
526+
}
410527
visitor.visit_expr(then);
411528
visitor.cx = expr_cx;
412529
}
413530

531+
hir::ExprKind::Match(subexpr, arms, _) => {
532+
visitor.visit_expr(subexpr);
533+
let lifetime = Scope { id: subexpr.hir_id.local_id, data: ScopeData::Node };
534+
for local in mark_local_terminating_scopes(subexpr) {
535+
if local != subexpr.hir_id.local_id {
536+
visitor.scope_tree.record_local_access_scope(local, lifetime);
537+
}
538+
}
539+
walk_list!(visitor, visit_arm, arms);
540+
}
541+
542+
hir::ExprKind::Index(subexpr, idxexpr) => {
543+
visitor.visit_expr(subexpr);
544+
visitor.visit_expr(idxexpr);
545+
visitor.scope_tree.record_eager_scope(
546+
idxexpr.hir_id.local_id,
547+
Scope { id: expr.hir_id.local_id, data: ScopeData::Node },
548+
);
549+
}
550+
414551
_ => intravisit::walk_expr(visitor, expr),
415552
}
416553

@@ -683,10 +820,17 @@ fn resolve_local<'tcx>(
683820
visitor.scope_tree.record_rvalue_scope(expr.hir_id.local_id, blk_scope);
684821

685822
match expr.kind {
686-
hir::ExprKind::AddrOf(_, _, ref subexpr)
687-
| hir::ExprKind::Unary(hir::UnOp::Deref, ref subexpr)
688-
| hir::ExprKind::Field(ref subexpr, _)
689-
| hir::ExprKind::Index(ref subexpr, _) => {
823+
hir::ExprKind::AddrOf(_, _, subexpr)
824+
| hir::ExprKind::Unary(hir::UnOp::Deref, subexpr)
825+
| hir::ExprKind::Field(subexpr, _)
826+
| hir::ExprKind::Index(subexpr, _) => {
827+
if let hir::ExprKind::Path(hir::QPath::Resolved(
828+
_,
829+
hir::Path { res: hir::def::Res::Local(_), .. },
830+
)) = &subexpr.kind
831+
{
832+
return;
833+
}
690834
expr = &subexpr;
691835
}
692836
_ => {

src/test/mir-opt/remove_storage_markers.main.RemoveStorageMarkers.diff

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@
7171
- StorageDead(_13); // scope 3 at $DIR/remove_storage_markers.rs:9:16: 9:17
7272
_6 = const (); // scope 3 at $DIR/remove_storage_markers.rs:8:20: 10:6
7373
- StorageDead(_12); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
74-
- StorageDead(_9); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
7574
- StorageDead(_7); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
7675
- StorageDead(_6); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
7776
_5 = const (); // scope 2 at $DIR/remove_storage_markers.rs:8:5: 10:6
@@ -80,7 +79,6 @@
8079

8180
bb3: {
8281
_0 = const (); // scope 2 at $DIR/remove_storage_markers.rs:8:5: 10:6
83-
- StorageDead(_9); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
8482
- StorageDead(_7); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
8583
- StorageDead(_6); // scope 2 at $DIR/remove_storage_markers.rs:10:5: 10:6
8684
- StorageDead(_4); // scope 1 at $DIR/remove_storage_markers.rs:10:5: 10:6
@@ -91,6 +89,7 @@
9189

9290
bb4: {
9391
- StorageDead(_14); // scope 5 at $DIR/remove_storage_markers.rs:8:14: 8:19
92+
- StorageDead(_9); // scope 2 at $DIR/remove_storage_markers.rs:8:18: 8:19
9493
- StorageDead(_8); // scope 2 at $DIR/remove_storage_markers.rs:8:18: 8:19
9594
_10 = discriminant(_7); // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19
9695
switchInt(move _10) -> [0_isize: bb3, otherwise: bb2]; // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19

src/test/ui/async-await/issue-64130-4-async-move.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,15 @@ impl Client {
1010
}
1111
}
1212

13-
async fn get() { }
13+
async fn get() {}
1414

1515
pub fn foo() -> impl Future + Send {
16-
//~^ ERROR future cannot be sent between threads safely
17-
let client = Client(Box::new(true));
18-
async move {
19-
match client.status() {
16+
async {
17+
//~^ ERROR future cannot be sent between threads safely
18+
match Client(Box::new(true)).status() {
2019
200 => {
2120
let _x = get().await;
22-
},
21+
}
2322
_ => (),
2423
}
2524
}

src/test/ui/async-await/issue-64130-4-async-move.stderr

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@ LL | pub fn foo() -> impl Future + Send {
66
|
77
= help: the trait `Sync` is not implemented for `(dyn Any + Send + 'static)`
88
note: future is not `Send` as this value is used across an await
9-
--> $DIR/issue-64130-4-async-move.rs:21:31
9+
--> $DIR/issue-64130-4-async-move.rs:20:31
1010
|
11-
LL | match client.status() {
12-
| ------ has type `&Client` which is not `Send`
11+
LL | match Client(Box::new(true)).status() {
12+
| ---------------------- has type `&Client` which is not `Send`
1313
LL | 200 => {
1414
LL | let _x = get().await;
15-
| ^^^^^^ await occurs here, with `client` maybe used later
15+
| ^^^^^^ await occurs here, with `Client(Box::new(true))` maybe used later
1616
...
1717
LL | }
18-
| - `client` is later dropped here
18+
| - `Client(Box::new(true))` is later dropped here
1919
help: consider moving this into a `let` binding to create a shorter lived borrow
20-
--> $DIR/issue-64130-4-async-move.rs:19:15
20+
--> $DIR/issue-64130-4-async-move.rs:18:15
2121
|
22-
LL | match client.status() {
23-
| ^^^^^^^^^^^^^^^
22+
LL | match Client(Box::new(true)).status() {
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2424

2525
error: aborting due to previous error
2626

0 commit comments

Comments
 (0)