Skip to content

Commit 6d29df8

Browse files
authored
Rollup merge of rust-lang#89208 - wesleywiser:rfc_2229_droporder, r=nikomatsakis
[rfc 2229] Drop fully captured upvars in the same order as the regular drop code Currently, with the new 2021 edition, if a closure captures all of the fields of an upvar, we'll drop those fields in the order they are used within the closure instead of the normal drop order (the definition order of the fields in the type). This changes that so we sort the captured fields by the definition order which causes them to drop in that same order as well. Fixes rust-lang/project-rfc-2229#42 r? `@nikomatsakis`
2 parents be29631 + 3893656 commit 6d29df8

6 files changed

+432
-1
lines changed

compiler/rustc_typeck/src/check/upvar.rs

+72-1
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,78 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
602602
}
603603
}
604604

605-
debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list);
605+
debug!(
606+
"For closure={:?}, min_captures before sorting={:?}",
607+
closure_def_id, root_var_min_capture_list
608+
);
609+
610+
// Now that we have the minimized list of captures, sort the captures by field id.
611+
// This causes the closure to capture the upvars in the same order as the fields are
612+
// declared which is also the drop order. Thus, in situations where we capture all the
613+
// fields of some type, the obserable drop order will remain the same as it previously
614+
// was even though we're dropping each capture individually.
615+
// See https://github.com/rust-lang/project-rfc-2229/issues/42 and
616+
// `src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs`.
617+
for (_, captures) in &mut root_var_min_capture_list {
618+
captures.sort_by(|capture1, capture2| {
619+
for (p1, p2) in capture1.place.projections.iter().zip(&capture2.place.projections) {
620+
// We do not need to look at the `Projection.ty` fields here because at each
621+
// step of the iteration, the projections will either be the same and therefore
622+
// the types must be as well or the current projection will be different and
623+
// we will return the result of comparing the field indexes.
624+
match (p1.kind, p2.kind) {
625+
// Paths are the same, continue to next loop.
626+
(ProjectionKind::Deref, ProjectionKind::Deref) => {}
627+
(ProjectionKind::Field(i1, _), ProjectionKind::Field(i2, _))
628+
if i1 == i2 => {}
629+
630+
// Fields are different, compare them.
631+
(ProjectionKind::Field(i1, _), ProjectionKind::Field(i2, _)) => {
632+
return i1.cmp(&i2);
633+
}
634+
635+
// We should have either a pair of `Deref`s or a pair of `Field`s.
636+
// Anything else is a bug.
637+
(
638+
l @ (ProjectionKind::Deref | ProjectionKind::Field(..)),
639+
r @ (ProjectionKind::Deref | ProjectionKind::Field(..)),
640+
) => bug!(
641+
"ProjectionKinds Deref and Field were mismatched: ({:?}, {:?})",
642+
l,
643+
r
644+
),
645+
(
646+
l
647+
@
648+
(ProjectionKind::Index
649+
| ProjectionKind::Subslice
650+
| ProjectionKind::Deref
651+
| ProjectionKind::Field(..)),
652+
r
653+
@
654+
(ProjectionKind::Index
655+
| ProjectionKind::Subslice
656+
| ProjectionKind::Deref
657+
| ProjectionKind::Field(..)),
658+
) => bug!(
659+
"ProjectionKinds Index or Subslice were unexpected: ({:?}, {:?})",
660+
l,
661+
r
662+
),
663+
}
664+
}
665+
666+
unreachable!(
667+
"we captured two identical projections: capture1 = {:?}, capture2 = {:?}",
668+
capture1, capture2
669+
);
670+
});
671+
}
672+
673+
debug!(
674+
"For closure={:?}, min_captures after sorting={:#?}",
675+
closure_def_id, root_var_min_capture_list
676+
);
606677
typeck_results.closure_min_captures.insert(closure_def_id, root_var_min_capture_list);
607678
}
608679

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// edition:2021
2+
3+
// Tests that in cases where we individually capture all the fields of a type,
4+
// we still drop them in the order they would have been dropped in the 2018 edition.
5+
6+
#![feature(rustc_attrs)]
7+
8+
#[derive(Debug)]
9+
struct HasDrop;
10+
impl Drop for HasDrop {
11+
fn drop(&mut self) {
12+
println!("dropped");
13+
}
14+
}
15+
16+
fn test_one() {
17+
let a = (HasDrop, HasDrop);
18+
let b = (HasDrop, HasDrop);
19+
20+
let c = #[rustc_capture_analysis]
21+
//~^ ERROR: attributes on expressions are experimental
22+
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
23+
|| {
24+
//~^ ERROR: First Pass analysis includes:
25+
//~| ERROR: Min Capture analysis includes:
26+
println!("{:?}", a.0);
27+
//~^ NOTE: Capturing a[(0, 0)] -> ImmBorrow
28+
//~| NOTE: Min Capture a[(0, 0)] -> ImmBorrow
29+
println!("{:?}", a.1);
30+
//~^ NOTE: Capturing a[(1, 0)] -> ImmBorrow
31+
//~| NOTE: Min Capture a[(1, 0)] -> ImmBorrow
32+
33+
println!("{:?}", b.0);
34+
//~^ NOTE: Capturing b[(0, 0)] -> ImmBorrow
35+
//~| NOTE: Min Capture b[(0, 0)] -> ImmBorrow
36+
println!("{:?}", b.1);
37+
//~^ NOTE: Capturing b[(1, 0)] -> ImmBorrow
38+
//~| NOTE: Min Capture b[(1, 0)] -> ImmBorrow
39+
};
40+
}
41+
42+
fn test_two() {
43+
let a = (HasDrop, HasDrop);
44+
let b = (HasDrop, HasDrop);
45+
46+
let c = #[rustc_capture_analysis]
47+
//~^ ERROR: attributes on expressions are experimental
48+
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
49+
|| {
50+
//~^ ERROR: First Pass analysis includes:
51+
//~| ERROR: Min Capture analysis includes:
52+
println!("{:?}", a.1);
53+
//~^ NOTE: Capturing a[(1, 0)] -> ImmBorrow
54+
//~| NOTE: Min Capture a[(1, 0)] -> ImmBorrow
55+
println!("{:?}", a.0);
56+
//~^ NOTE: Capturing a[(0, 0)] -> ImmBorrow
57+
//~| NOTE: Min Capture a[(0, 0)] -> ImmBorrow
58+
59+
println!("{:?}", b.1);
60+
//~^ NOTE: Capturing b[(1, 0)] -> ImmBorrow
61+
//~| NOTE: Min Capture b[(1, 0)] -> ImmBorrow
62+
println!("{:?}", b.0);
63+
//~^ NOTE: Capturing b[(0, 0)] -> ImmBorrow
64+
//~| NOTE: Min Capture b[(0, 0)] -> ImmBorrow
65+
};
66+
}
67+
68+
fn test_three() {
69+
let a = (HasDrop, HasDrop);
70+
let b = (HasDrop, HasDrop);
71+
72+
let c = #[rustc_capture_analysis]
73+
//~^ ERROR: attributes on expressions are experimental
74+
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
75+
|| {
76+
//~^ ERROR: First Pass analysis includes:
77+
//~| ERROR: Min Capture analysis includes:
78+
println!("{:?}", b.1);
79+
//~^ NOTE: Capturing b[(1, 0)] -> ImmBorrow
80+
//~| NOTE: Min Capture b[(1, 0)] -> ImmBorrow
81+
println!("{:?}", a.1);
82+
//~^ NOTE: Capturing a[(1, 0)] -> ImmBorrow
83+
//~| NOTE: Min Capture a[(1, 0)] -> ImmBorrow
84+
println!("{:?}", a.0);
85+
//~^ NOTE: Capturing a[(0, 0)] -> ImmBorrow
86+
//~| NOTE: Min Capture a[(0, 0)] -> ImmBorrow
87+
88+
println!("{:?}", b.0);
89+
//~^ NOTE: Capturing b[(0, 0)] -> ImmBorrow
90+
//~| NOTE: Min Capture b[(0, 0)] -> ImmBorrow
91+
};
92+
}
93+
94+
fn main() {
95+
test_one();
96+
test_two();
97+
test_three();
98+
}

0 commit comments

Comments
 (0)