Skip to content

Commit 851b33a

Browse files
committedFeb 9, 2021
rust-timer simulated merge of a188791
Original message: Rollup merge of rust-lang#80629 - sexxi-goose:migrations_1, r=nikomatsakis Add lint for 2229 migrations Implements the first for RFC 2229 where we make the decision to migrate a root variable based on if the type of the variable needs Drop and if the root variable would be moved into the closure when the feature isn't enabled. r? `@nikomatsakis`
1 parent 46aae25 commit 851b33a

File tree

10 files changed

+812
-53
lines changed

10 files changed

+812
-53
lines changed
 

‎compiler/rustc_lint_defs/src/builtin.rs

+46
Original file line numberDiff line numberDiff line change
@@ -2968,6 +2968,7 @@ declare_lint_pass! {
29682968
UNSUPPORTED_NAKED_FUNCTIONS,
29692969
MISSING_ABI,
29702970
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
2971+
DISJOINT_CAPTURE_DROP_REORDER,
29712972
]
29722973
}
29732974

@@ -2994,6 +2995,51 @@ declare_lint! {
29942995
"detects doc comments that aren't used by rustdoc"
29952996
}
29962997

2998+
declare_lint! {
2999+
/// The `disjoint_capture_drop_reorder` lint detects variables that aren't completely
3000+
/// captured when the feature `capture_disjoint_fields` is enabled and it affects the Drop
3001+
/// order of at least one path starting at this variable.
3002+
///
3003+
/// ### Example
3004+
///
3005+
/// ```rust,compile_fail
3006+
/// # #![deny(disjoint_capture_drop_reorder)]
3007+
/// # #![allow(unused)]
3008+
/// struct FancyInteger(i32);
3009+
///
3010+
/// impl Drop for FancyInteger {
3011+
/// fn drop(&mut self) {
3012+
/// println!("Just dropped {}", self.0);
3013+
/// }
3014+
/// }
3015+
///
3016+
/// struct Point { x: FancyInteger, y: FancyInteger }
3017+
///
3018+
/// fn main() {
3019+
/// let p = Point { x: FancyInteger(10), y: FancyInteger(20) };
3020+
///
3021+
/// let c = || {
3022+
/// let x = p.x;
3023+
/// };
3024+
///
3025+
/// c();
3026+
///
3027+
/// // ... More code ...
3028+
/// }
3029+
/// ```
3030+
///
3031+
/// {{produces}}
3032+
///
3033+
/// ### Explanation
3034+
///
3035+
/// In the above example `p.y` will be dropped at the end of `f` instead of with `c` if
3036+
/// the feature `capture_disjoint_fields` is enabled.
3037+
pub DISJOINT_CAPTURE_DROP_REORDER,
3038+
Allow,
3039+
"Drop reorder because of `capture_disjoint_fields`"
3040+
3041+
}
3042+
29973043
declare_lint_pass!(UnusedDocComment => [UNUSED_DOC_COMMENTS]);
29983044

29993045
declare_lint! {

‎compiler/rustc_typeck/src/check/upvar.rs

+198-44
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
//! then mean that all later passes would have to check for these figments
3131
//! and report an error, and it just seems like more mess in the end.)
3232
33+
use super::writeback::Resolver;
3334
use super::FnCtxt;
3435

3536
use crate::expr_use_visitor as euv;
@@ -40,7 +41,9 @@ use rustc_hir::def_id::LocalDefId;
4041
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
4142
use rustc_infer::infer::UpvarRegion;
4243
use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, ProjectionKind};
43-
use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts};
44+
use rustc_middle::ty::fold::TypeFoldable;
45+
use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults, UpvarSubsts};
46+
use rustc_session::lint;
4447
use rustc_span::sym;
4548
use rustc_span::{MultiSpan, Span, Symbol};
4649

@@ -55,6 +58,11 @@ enum PlaceAncestryRelation {
5558
Divergent,
5659
}
5760

61+
/// Intermediate format to store a captured `Place` and associated `ty::CaptureInfo`
62+
/// during capture analysis. Information in this map feeds into the minimum capture
63+
/// analysis pass.
64+
type InferredCaptureInformation<'tcx> = FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>;
65+
5866
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5967
pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) {
6068
InferBorrowKindVisitor { fcx: self }.visit_body(body);
@@ -92,7 +100,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
92100
&self,
93101
closure_hir_id: hir::HirId,
94102
span: Span,
95-
body: &hir::Body<'_>,
103+
body: &'tcx hir::Body<'tcx>,
96104
capture_clause: hir::CaptureBy,
97105
) {
98106
debug!("analyze_closure(id={:?}, body.id={:?})", closure_hir_id, body.id());
@@ -124,28 +132,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
124132

125133
let local_def_id = closure_def_id.expect_local();
126134

127-
let mut capture_information: FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>> =
128-
Default::default();
129-
if !self.tcx.features().capture_disjoint_fields {
130-
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
131-
for (&var_hir_id, _) in upvars.iter() {
132-
let place = self.place_for_root_variable(local_def_id, var_hir_id);
133-
134-
debug!("seed place {:?}", place);
135-
136-
let upvar_id = ty::UpvarId::new(var_hir_id, local_def_id);
137-
let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span);
138-
let info = ty::CaptureInfo {
139-
capture_kind_expr_id: None,
140-
path_expr_id: None,
141-
capture_kind,
142-
};
143-
144-
capture_information.insert(place, info);
145-
}
146-
}
147-
}
148-
149135
let body_owner_def_id = self.tcx.hir().body_owner_def_id(body.id());
150136
assert_eq!(body_owner_def_id.to_def_id(), closure_def_id);
151137
let mut delegate = InferBorrowKind {
@@ -155,7 +141,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
155141
capture_clause,
156142
current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM,
157143
current_origin: None,
158-
capture_information,
144+
capture_information: Default::default(),
159145
};
160146
euv::ExprUseVisitor::new(
161147
&mut delegate,
@@ -172,6 +158,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
172158
);
173159
self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span);
174160

161+
self.compute_min_captures(closure_def_id, delegate.capture_information);
162+
163+
let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
164+
if should_do_migration_analysis(self.tcx, closure_hir_id) {
165+
self.perform_2229_migration_anaysis(closure_def_id, capture_clause, span, body);
166+
}
167+
168+
// We now fake capture information for all variables that are mentioned within the closure
169+
// We do this after handling migrations so that min_captures computes before
170+
if !self.tcx.features().capture_disjoint_fields {
171+
let mut capture_information: InferredCaptureInformation<'tcx> = Default::default();
172+
173+
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
174+
for var_hir_id in upvars.keys() {
175+
let place = self.place_for_root_variable(local_def_id, *var_hir_id);
176+
177+
debug!("seed place {:?}", place);
178+
179+
let upvar_id = ty::UpvarId::new(*var_hir_id, local_def_id);
180+
let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span);
181+
let fake_info = ty::CaptureInfo {
182+
capture_kind_expr_id: None,
183+
path_expr_id: None,
184+
capture_kind,
185+
};
186+
187+
capture_information.insert(place, fake_info);
188+
}
189+
}
190+
191+
// This will update the min captures based on this new fake information.
192+
self.compute_min_captures(closure_def_id, capture_information);
193+
}
194+
175195
if let Some(closure_substs) = infer_kind {
176196
// Unify the (as yet unbound) type variable in the closure
177197
// substs with the kind we inferred.
@@ -197,7 +217,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
197217
}
198218
}
199219

200-
self.compute_min_captures(closure_def_id, delegate);
201220
self.log_closure_min_capture_info(closure_def_id, span);
202221

203222
self.min_captures_to_closure_captures_bridge(closure_def_id);
@@ -344,6 +363,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
344363
/// Places (and corresponding capture kind) that we need to keep track of to support all
345364
/// the required captured paths.
346365
///
366+
///
367+
/// Note: If this function is called multiple times for the same closure, it will update
368+
/// the existing min_capture map that is stored in TypeckResults.
369+
///
347370
/// Eg:
348371
/// ```rust,no_run
349372
/// struct Point { x: i32, y: i32 }
@@ -408,11 +431,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
408431
fn compute_min_captures(
409432
&self,
410433
closure_def_id: DefId,
411-
inferred_info: InferBorrowKind<'_, 'tcx>,
434+
capture_information: InferredCaptureInformation<'tcx>,
412435
) {
413-
let mut root_var_min_capture_list: ty::RootVariableMinCaptureList<'_> = Default::default();
436+
if capture_information.is_empty() {
437+
return;
438+
}
439+
440+
let mut typeck_results = self.typeck_results.borrow_mut();
414441

415-
for (place, capture_info) in inferred_info.capture_information.into_iter() {
442+
let mut root_var_min_capture_list =
443+
typeck_results.closure_min_captures.remove(&closure_def_id).unwrap_or_default();
444+
445+
for (place, capture_info) in capture_information.into_iter() {
416446
let var_hir_id = match place.base {
417447
PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
418448
base => bug!("Expected upvar, found={:?}", base),
@@ -422,7 +452,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
422452

423453
let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
424454
None => {
425-
let mutability = self.determine_capture_mutability(&place);
455+
let mutability = self.determine_capture_mutability(&typeck_results, &place);
426456
let min_cap_list =
427457
vec![ty::CapturedPlace { place, info: capture_info, mutability }];
428458
root_var_min_capture_list.insert(var_hir_id, min_cap_list);
@@ -487,21 +517,129 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
487517

488518
// Only need to insert when we don't have an ancestor in the existing min capture list
489519
if !ancestor_found {
490-
let mutability = self.determine_capture_mutability(&place);
520+
let mutability = self.determine_capture_mutability(&typeck_results, &place);
491521
let captured_place =
492522
ty::CapturedPlace { place, info: updated_capture_info, mutability };
493523
min_cap_list.push(captured_place);
494524
}
495525
}
496526

497527
debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list);
528+
typeck_results.closure_min_captures.insert(closure_def_id, root_var_min_capture_list);
529+
}
498530

499-
if !root_var_min_capture_list.is_empty() {
500-
self.typeck_results
501-
.borrow_mut()
502-
.closure_min_captures
503-
.insert(closure_def_id, root_var_min_capture_list);
531+
/// Perform the migration analysis for RFC 2229, and emit lint
532+
/// `disjoint_capture_drop_reorder` if needed.
533+
fn perform_2229_migration_anaysis(
534+
&self,
535+
closure_def_id: DefId,
536+
capture_clause: hir::CaptureBy,
537+
span: Span,
538+
body: &'tcx hir::Body<'tcx>,
539+
) {
540+
let need_migrations = self.compute_2229_migrations_first_pass(
541+
closure_def_id,
542+
span,
543+
capture_clause,
544+
body,
545+
self.typeck_results.borrow().closure_min_captures.get(&closure_def_id),
546+
);
547+
548+
if !need_migrations.is_empty() {
549+
let need_migrations_hir_id = need_migrations.iter().map(|m| m.0).collect::<Vec<_>>();
550+
551+
let migrations_text = migration_suggestion_for_2229(self.tcx, &need_migrations_hir_id);
552+
553+
let local_def_id = closure_def_id.expect_local();
554+
let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
555+
self.tcx.struct_span_lint_hir(
556+
lint::builtin::DISJOINT_CAPTURE_DROP_REORDER,
557+
closure_hir_id,
558+
span,
559+
|lint| {
560+
let mut diagnostics_builder = lint.build(
561+
"drop order affected for closure because of `capture_disjoint_fields`",
562+
);
563+
diagnostics_builder.note(&migrations_text);
564+
diagnostics_builder.emit();
565+
},
566+
);
567+
}
568+
}
569+
570+
/// Figures out the list of root variables (and their types) that aren't completely
571+
/// captured by the closure when `capture_disjoint_fields` is enabled and drop order of
572+
/// some path starting at that root variable **might** be affected.
573+
///
574+
/// The output list would include a root variable if:
575+
/// - It would have been moved into the closure when `capture_disjoint_fields` wasn't
576+
/// enabled, **and**
577+
/// - It wasn't completely captured by the closure, **and**
578+
/// - The type of the root variable needs Drop.
579+
fn compute_2229_migrations_first_pass(
580+
&self,
581+
closure_def_id: DefId,
582+
closure_span: Span,
583+
closure_clause: hir::CaptureBy,
584+
body: &'tcx hir::Body<'tcx>,
585+
min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>,
586+
) -> Vec<(hir::HirId, Ty<'tcx>)> {
587+
fn resolve_ty<T: TypeFoldable<'tcx>>(
588+
fcx: &FnCtxt<'_, 'tcx>,
589+
span: Span,
590+
body: &'tcx hir::Body<'tcx>,
591+
ty: T,
592+
) -> T {
593+
let mut resolver = Resolver::new(fcx, &span, body);
594+
ty.fold_with(&mut resolver)
595+
}
596+
597+
let upvars = if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
598+
upvars
599+
} else {
600+
return vec![];
601+
};
602+
603+
let mut need_migrations = Vec::new();
604+
605+
for (&var_hir_id, _) in upvars.iter() {
606+
let ty = resolve_ty(self, closure_span, body, self.node_ty(var_hir_id));
607+
608+
if !ty.needs_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) {
609+
continue;
610+
}
611+
612+
let root_var_min_capture_list = if let Some(root_var_min_capture_list) =
613+
min_captures.and_then(|m| m.get(&var_hir_id))
614+
{
615+
root_var_min_capture_list
616+
} else {
617+
// The upvar is mentioned within the closure but no path starting from it is
618+
// used.
619+
620+
match closure_clause {
621+
// Only migrate if closure is a move closure
622+
hir::CaptureBy::Value => need_migrations.push((var_hir_id, ty)),
623+
624+
hir::CaptureBy::Ref => {}
625+
}
626+
627+
continue;
628+
};
629+
630+
let is_moved = root_var_min_capture_list
631+
.iter()
632+
.any(|capture| matches!(capture.info.capture_kind, ty::UpvarCapture::ByValue(_)));
633+
634+
let is_not_completely_captured =
635+
root_var_min_capture_list.iter().any(|capture| capture.place.projections.len() > 0);
636+
637+
if is_moved && is_not_completely_captured {
638+
need_migrations.push((var_hir_id, ty));
639+
}
504640
}
641+
642+
need_migrations
505643
}
506644

507645
fn init_capture_kind(
@@ -613,18 +751,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
613751
/// A captured place is mutable if
614752
/// 1. Projections don't include a Deref of an immut-borrow, **and**
615753
/// 2. PlaceBase is mut or projections include a Deref of a mut-borrow.
616-
fn determine_capture_mutability(&self, place: &Place<'tcx>) -> hir::Mutability {
754+
fn determine_capture_mutability(
755+
&self,
756+
typeck_results: &'a TypeckResults<'tcx>,
757+
place: &Place<'tcx>,
758+
) -> hir::Mutability {
617759
let var_hir_id = match place.base {
618760
PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
619761
_ => unreachable!(),
620762
};
621763

622-
let bm = *self
623-
.typeck_results
624-
.borrow()
625-
.pat_binding_modes()
626-
.get(var_hir_id)
627-
.expect("missing binding mode");
764+
let bm = *typeck_results.pat_binding_modes().get(var_hir_id).expect("missing binding mode");
628765

629766
let mut is_mutbl = match bm {
630767
ty::BindByValue(mutability) => mutability,
@@ -698,9 +835,11 @@ struct InferBorrowKind<'a, 'tcx> {
698835
///
699836
/// For closure `fix_s`, (at a high level) the map contains
700837
///
838+
/// ```
701839
/// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow }
702840
/// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
703-
capture_information: FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>,
841+
/// ```
842+
capture_information: InferredCaptureInformation<'tcx>,
704843
}
705844

706845
impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
@@ -1119,6 +1258,21 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
11191258
tcx.hir().name(var_hir_id)
11201259
}
11211260

1261+
fn should_do_migration_analysis(tcx: TyCtxt<'_>, closure_id: hir::HirId) -> bool {
1262+
let (level, _) =
1263+
tcx.lint_level_at_node(lint::builtin::DISJOINT_CAPTURE_DROP_REORDER, closure_id);
1264+
1265+
!matches!(level, lint::Level::Allow)
1266+
}
1267+
1268+
fn migration_suggestion_for_2229(tcx: TyCtxt<'_>, need_migrations: &Vec<hir::HirId>) -> String {
1269+
let need_migrations_strings =
1270+
need_migrations.iter().map(|v| format!("{}", var_name(tcx, *v))).collect::<Vec<_>>();
1271+
let migrations_list_concat = need_migrations_strings.join(", ");
1272+
1273+
format!("drop(&({}));", migrations_list_concat)
1274+
}
1275+
11221276
/// Helper function to determine if we need to escalate CaptureKind from
11231277
/// CaptureInfo A to B and returns the escalated CaptureInfo.
11241278
/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)

‎compiler/rustc_typeck/src/check/writeback.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
650650
}
651651
}
652652

653-
trait Locatable {
653+
crate trait Locatable {
654654
fn to_span(&self, tcx: TyCtxt<'_>) -> Span;
655655
}
656656

@@ -668,7 +668,7 @@ impl Locatable for hir::HirId {
668668

669669
/// The Resolver. This is the type folding engine that detects
670670
/// unresolved types and so forth.
671-
struct Resolver<'cx, 'tcx> {
671+
crate struct Resolver<'cx, 'tcx> {
672672
tcx: TyCtxt<'tcx>,
673673
infcx: &'cx InferCtxt<'cx, 'tcx>,
674674
span: &'cx dyn Locatable,
@@ -679,7 +679,7 @@ struct Resolver<'cx, 'tcx> {
679679
}
680680

681681
impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
682-
fn new(
682+
crate fn new(
683683
fcx: &'cx FnCtxt<'cx, 'tcx>,
684684
span: &'cx dyn Locatable,
685685
body: &'tcx hir::Body<'tcx>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#![deny(disjoint_capture_drop_reorder)]
2+
//~^ NOTE: the lint level is defined here
3+
4+
// Test cases for types that implement a insignificant drop (stlib defined)
5+
6+
// `t` needs Drop because one of its elements needs drop,
7+
// therefore precise capture might affect drop ordering
8+
fn test1_all_need_migration() {
9+
let t = (String::new(), String::new());
10+
let t1 = (String::new(), String::new());
11+
let t2 = (String::new(), String::new());
12+
13+
let c = || {
14+
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
15+
//~| NOTE: drop(&(t, t1, t2));
16+
let _t = t.0;
17+
let _t1 = t1.0;
18+
let _t2 = t2.0;
19+
};
20+
21+
c();
22+
}
23+
24+
// String implements drop and therefore should be migrated.
25+
// But in this test cases, `t2` is completely captured and when it is dropped won't be affected
26+
fn test2_only_precise_paths_need_migration() {
27+
let t = (String::new(), String::new());
28+
let t1 = (String::new(), String::new());
29+
let t2 = (String::new(), String::new());
30+
31+
let c = || {
32+
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
33+
//~| NOTE: drop(&(t, t1));
34+
let _t = t.0;
35+
let _t1 = t1.0;
36+
let _t2 = t2;
37+
};
38+
39+
c();
40+
}
41+
42+
// If a variable would've not been captured by value then it would've not been
43+
// dropped with the closure and therefore doesn't need migration.
44+
fn test3_only_by_value_need_migration() {
45+
let t = (String::new(), String::new());
46+
let t1 = (String::new(), String::new());
47+
let c = || {
48+
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
49+
//~| NOTE: drop(&(t));
50+
let _t = t.0;
51+
println!("{}", t1.1);
52+
};
53+
54+
c();
55+
}
56+
57+
// Copy types get copied into the closure instead of move. Therefore we don't need to
58+
// migrate then as their drop order isn't tied to the closure.
59+
fn test4_only_non_copy_types_need_migration() {
60+
let t = (String::new(), String::new());
61+
62+
// `t1` is Copy because all of its elements are Copy
63+
let t1 = (0i32, 0i32);
64+
65+
let c = || {
66+
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
67+
//~| NOTE: drop(&(t));
68+
let _t = t.0;
69+
let _t1 = t1.0;
70+
};
71+
72+
c();
73+
}
74+
75+
fn test5_only_drop_types_need_migration() {
76+
struct S(i32, i32);
77+
78+
let t = (String::new(), String::new());
79+
80+
// `s` doesn't implement Drop or any elements within it, and doesn't need migration
81+
let s = S(0i32, 0i32);
82+
83+
let c = || {
84+
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
85+
//~| NOTE: drop(&(t));
86+
let _t = t.0;
87+
let _s = s.0;
88+
};
89+
90+
c();
91+
}
92+
93+
// Since we are using a move closure here, both `t` and `t1` get moved
94+
// even though they are being used by ref inside the closure.
95+
fn test6_move_closures_non_copy_types_might_need_migration() {
96+
let t = (String::new(), String::new());
97+
let t1 = (String::new(), String::new());
98+
let c = move || {
99+
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
100+
//~| NOTE: drop(&(t1, t));
101+
println!("{} {}", t1.1, t.1);
102+
};
103+
104+
c();
105+
}
106+
107+
// Test migration analysis in case of Drop + Non Drop aggregates.
108+
// Note we need migration here only because the non-copy (because Drop type) is captured,
109+
// otherwise we won't need to, since we can get away with just by ref capture in that case.
110+
fn test7_drop_non_drop_aggregate_need_migration() {
111+
let t = (String::new(), String::new(), 0i32);
112+
113+
let c = || {
114+
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
115+
//~| NOTE: drop(&(t));
116+
let _t = t.0;
117+
};
118+
119+
c();
120+
}
121+
122+
fn main() {
123+
test1_all_need_migration();
124+
test2_only_precise_paths_need_migration();
125+
test3_only_by_value_need_migration();
126+
test4_only_non_copy_types_need_migration();
127+
test5_only_drop_types_need_migration();
128+
test6_move_closures_non_copy_types_might_need_migration();
129+
test7_drop_non_drop_aggregate_need_migration();
130+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
error: drop order affected for closure because of `capture_disjoint_fields`
2+
--> $DIR/insignificant_drop.rs:13:13
3+
|
4+
LL | let c = || {
5+
| _____________^
6+
LL | |
7+
LL | |
8+
LL | | let _t = t.0;
9+
LL | | let _t1 = t1.0;
10+
LL | | let _t2 = t2.0;
11+
LL | | };
12+
| |_____^
13+
|
14+
note: the lint level is defined here
15+
--> $DIR/insignificant_drop.rs:1:9
16+
|
17+
LL | #![deny(disjoint_capture_drop_reorder)]
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
= note: drop(&(t, t1, t2));
20+
21+
error: drop order affected for closure because of `capture_disjoint_fields`
22+
--> $DIR/insignificant_drop.rs:31:13
23+
|
24+
LL | let c = || {
25+
| _____________^
26+
LL | |
27+
LL | |
28+
LL | | let _t = t.0;
29+
LL | | let _t1 = t1.0;
30+
LL | | let _t2 = t2;
31+
LL | | };
32+
| |_____^
33+
|
34+
= note: drop(&(t, t1));
35+
36+
error: drop order affected for closure because of `capture_disjoint_fields`
37+
--> $DIR/insignificant_drop.rs:47:13
38+
|
39+
LL | let c = || {
40+
| _____________^
41+
LL | |
42+
LL | |
43+
LL | | let _t = t.0;
44+
LL | | println!("{}", t1.1);
45+
LL | | };
46+
| |_____^
47+
|
48+
= note: drop(&(t));
49+
50+
error: drop order affected for closure because of `capture_disjoint_fields`
51+
--> $DIR/insignificant_drop.rs:65:13
52+
|
53+
LL | let c = || {
54+
| _____________^
55+
LL | |
56+
LL | |
57+
LL | | let _t = t.0;
58+
LL | | let _t1 = t1.0;
59+
LL | | };
60+
| |_____^
61+
|
62+
= note: drop(&(t));
63+
64+
error: drop order affected for closure because of `capture_disjoint_fields`
65+
--> $DIR/insignificant_drop.rs:83:13
66+
|
67+
LL | let c = || {
68+
| _____________^
69+
LL | |
70+
LL | |
71+
LL | | let _t = t.0;
72+
LL | | let _s = s.0;
73+
LL | | };
74+
| |_____^
75+
|
76+
= note: drop(&(t));
77+
78+
error: drop order affected for closure because of `capture_disjoint_fields`
79+
--> $DIR/insignificant_drop.rs:98:13
80+
|
81+
LL | let c = move || {
82+
| _____________^
83+
LL | |
84+
LL | |
85+
LL | | println!("{} {}", t1.1, t.1);
86+
LL | | };
87+
| |_____^
88+
|
89+
= note: drop(&(t1, t));
90+
91+
error: drop order affected for closure because of `capture_disjoint_fields`
92+
--> $DIR/insignificant_drop.rs:113:13
93+
|
94+
LL | let c = || {
95+
| _____________^
96+
LL | |
97+
LL | |
98+
LL | | let _t = t.0;
99+
LL | | };
100+
| |_____^
101+
|
102+
= note: drop(&(t));
103+
104+
error: aborting due to 7 previous errors
105+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// run-pass
2+
3+
// Set of test cases that don't need migrations
4+
5+
#![deny(disjoint_capture_drop_reorder)]
6+
7+
8+
// Copy types as copied by the closure instead of being moved into the closure
9+
// Therefore their drop order isn't tied to the closure and won't be requiring any
10+
// migrations.
11+
fn test1_only_copy_types() {
12+
let t = (0i32, 0i32);
13+
14+
let c = || {
15+
let _t = t.0;
16+
};
17+
18+
c();
19+
}
20+
21+
// Same as test1 but using a move closure
22+
fn test2_only_copy_types_move_closure() {
23+
let t = (0i32, 0i32);
24+
25+
let c = move || {
26+
println!("{}", t.0);
27+
};
28+
29+
c();
30+
}
31+
32+
// Don't need to migrate if captured by ref
33+
fn test3_only_copy_types_move_closure() {
34+
let t = (String::new(), String::new());
35+
36+
let c = || {
37+
println!("{}", t.0);
38+
};
39+
40+
c();
41+
}
42+
43+
// Test migration analysis in case of Insignificant Drop + Non Drop aggregates.
44+
// Note in this test the closure captures a non Drop type and therefore the variable
45+
// is only captured by ref.
46+
fn test4_insignificant_drop_non_drop_aggregate() {
47+
let t = (String::new(), 0i32);
48+
49+
let c = || {
50+
let _t = t.1;
51+
};
52+
53+
c();
54+
}
55+
56+
57+
struct Foo(i32);
58+
impl Drop for Foo {
59+
fn drop(&mut self) {
60+
println!("{:?} dropped", self.0);
61+
}
62+
}
63+
64+
// Test migration analysis in case of Significant Drop + Non Drop aggregates.
65+
// Note in this test the closure captures a non Drop type and therefore the variable
66+
// is only captured by ref.
67+
fn test5_significant_drop_non_drop_aggregate() {
68+
let t = (Foo(0), 0i32);
69+
70+
let c = || {
71+
let _t = t.1;
72+
};
73+
74+
c();
75+
}
76+
77+
fn main() {
78+
test1_only_copy_types();
79+
test2_only_copy_types_move_closure();
80+
test3_only_copy_types_move_closure();
81+
test4_insignificant_drop_non_drop_aggregate();
82+
test5_significant_drop_non_drop_aggregate();
83+
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#![deny(disjoint_capture_drop_reorder)]
2+
//~^ NOTE: the lint level is defined here
3+
4+
// Test cases for types that implement a significant drop (user defined)
5+
6+
#[derive(Debug)]
7+
struct Foo(i32);
8+
impl Drop for Foo {
9+
fn drop(&mut self) {
10+
println!("{:?} dropped", self.0);
11+
}
12+
}
13+
14+
#[derive(Debug)]
15+
struct ConstainsDropField(Foo, Foo);
16+
17+
// `t` needs Drop because one of its elements needs drop,
18+
// therefore precise capture might affect drop ordering
19+
fn test1_all_need_migration() {
20+
let t = (Foo(0), Foo(0));
21+
let t1 = (Foo(0), Foo(0));
22+
let t2 = (Foo(0), Foo(0));
23+
24+
let c = || {
25+
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
26+
//~| NOTE: drop(&(t, t1, t2));
27+
let _t = t.0;
28+
let _t1 = t1.0;
29+
let _t2 = t2.0;
30+
};
31+
32+
c();
33+
}
34+
35+
// String implements drop and therefore should be migrated.
36+
// But in this test cases, `t2` is completely captured and when it is dropped won't be affected
37+
fn test2_only_precise_paths_need_migration() {
38+
let t = (Foo(0), Foo(0));
39+
let t1 = (Foo(0), Foo(0));
40+
let t2 = (Foo(0), Foo(0));
41+
42+
let c = || {
43+
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
44+
//~| NOTE: drop(&(t, t1));
45+
let _t = t.0;
46+
let _t1 = t1.0;
47+
let _t2 = t2;
48+
};
49+
50+
c();
51+
}
52+
53+
// If a variable would've not been captured by value then it would've not been
54+
// dropped with the closure and therefore doesn't need migration.
55+
fn test3_only_by_value_need_migration() {
56+
let t = (Foo(0), Foo(0));
57+
let t1 = (Foo(0), Foo(0));
58+
let c = || {
59+
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
60+
//~| NOTE: drop(&(t));
61+
let _t = t.0;
62+
println!("{:?}", t1.1);
63+
};
64+
65+
c();
66+
}
67+
68+
// The root variable might not implement drop themselves but some path starting
69+
// at the root variable might implement Drop.
70+
//
71+
// If this path isn't captured we need to migrate for the root variable.
72+
fn test4_type_contains_drop_need_migration() {
73+
let t = ConstainsDropField(Foo(0), Foo(0));
74+
75+
let c = || {
76+
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
77+
//~| NOTE: drop(&(t));
78+
let _t = t.0;
79+
};
80+
81+
c();
82+
}
83+
84+
// Test migration analysis in case of Drop + Non Drop aggregates.
85+
// Note we need migration here only because the non-copy (because Drop type) is captured,
86+
// otherwise we won't need to, since we can get away with just by ref capture in that case.
87+
fn test5_drop_non_drop_aggregate_need_migration() {
88+
let t = (Foo(0), Foo(0), 0i32);
89+
90+
let c = || {
91+
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
92+
//~| NOTE: drop(&(t));
93+
let _t = t.0;
94+
};
95+
96+
c();
97+
}
98+
99+
// Test migration analysis in case of Significant and Insignificant Drop aggregates.
100+
fn test6_significant_insignificant_drop_aggregate_need_migration() {
101+
struct S(i32, i32);
102+
103+
let t = (Foo(0), String::new());
104+
105+
let c = || {
106+
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
107+
//~| NOTE: drop(&(t));
108+
let _t = t.1;
109+
};
110+
111+
c();
112+
}
113+
114+
// Since we are using a move closure here, both `t` and `t1` get moved
115+
// even though they are being used by ref inside the closure.
116+
fn test7_move_closures_non_copy_types_might_need_migration() {
117+
let t = (Foo(0), Foo(0));
118+
let t1 = (Foo(0), Foo(0), Foo(0));
119+
120+
let c = move || {
121+
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
122+
//~| NOTE: drop(&(t1, t));
123+
println!("{:?} {:?}", t1.1, t.1);
124+
};
125+
126+
c();
127+
}
128+
129+
fn main() {
130+
test1_all_need_migration();
131+
test2_only_precise_paths_need_migration();
132+
test3_only_by_value_need_migration();
133+
test4_type_contains_drop_need_migration();
134+
test5_drop_non_drop_aggregate_need_migration();
135+
test6_significant_insignificant_drop_aggregate_need_migration();
136+
test7_move_closures_non_copy_types_might_need_migration();
137+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
error: drop order affected for closure because of `capture_disjoint_fields`
2+
--> $DIR/significant_drop.rs:24:13
3+
|
4+
LL | let c = || {
5+
| _____________^
6+
LL | |
7+
LL | |
8+
LL | | let _t = t.0;
9+
LL | | let _t1 = t1.0;
10+
LL | | let _t2 = t2.0;
11+
LL | | };
12+
| |_____^
13+
|
14+
note: the lint level is defined here
15+
--> $DIR/significant_drop.rs:1:9
16+
|
17+
LL | #![deny(disjoint_capture_drop_reorder)]
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
= note: drop(&(t, t1, t2));
20+
21+
error: drop order affected for closure because of `capture_disjoint_fields`
22+
--> $DIR/significant_drop.rs:42:13
23+
|
24+
LL | let c = || {
25+
| _____________^
26+
LL | |
27+
LL | |
28+
LL | | let _t = t.0;
29+
LL | | let _t1 = t1.0;
30+
LL | | let _t2 = t2;
31+
LL | | };
32+
| |_____^
33+
|
34+
= note: drop(&(t, t1));
35+
36+
error: drop order affected for closure because of `capture_disjoint_fields`
37+
--> $DIR/significant_drop.rs:58:13
38+
|
39+
LL | let c = || {
40+
| _____________^
41+
LL | |
42+
LL | |
43+
LL | | let _t = t.0;
44+
LL | | println!("{:?}", t1.1);
45+
LL | | };
46+
| |_____^
47+
|
48+
= note: drop(&(t));
49+
50+
error: drop order affected for closure because of `capture_disjoint_fields`
51+
--> $DIR/significant_drop.rs:75:13
52+
|
53+
LL | let c = || {
54+
| _____________^
55+
LL | |
56+
LL | |
57+
LL | | let _t = t.0;
58+
LL | | };
59+
| |_____^
60+
|
61+
= note: drop(&(t));
62+
63+
error: drop order affected for closure because of `capture_disjoint_fields`
64+
--> $DIR/significant_drop.rs:90:13
65+
|
66+
LL | let c = || {
67+
| _____________^
68+
LL | |
69+
LL | |
70+
LL | | let _t = t.0;
71+
LL | | };
72+
| |_____^
73+
|
74+
= note: drop(&(t));
75+
76+
error: drop order affected for closure because of `capture_disjoint_fields`
77+
--> $DIR/significant_drop.rs:105:13
78+
|
79+
LL | let c = || {
80+
| _____________^
81+
LL | |
82+
LL | |
83+
LL | | let _t = t.1;
84+
LL | | };
85+
| |_____^
86+
|
87+
= note: drop(&(t));
88+
89+
error: drop order affected for closure because of `capture_disjoint_fields`
90+
--> $DIR/significant_drop.rs:120:13
91+
|
92+
LL | let c = move || {
93+
| _____________^
94+
LL | |
95+
LL | |
96+
LL | | println!("{:?} {:?}", t1.1, t.1);
97+
LL | | };
98+
| |_____^
99+
|
100+
= note: drop(&(t1, t));
101+
102+
error: aborting due to 7 previous errors
103+

‎src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ LL | let mut closure1 = || p = &y;
77
= note: defining type: test::{closure#0}::{closure#0} with closure substs [
88
i16,
99
extern "rust-call" fn(()),
10-
(&'_#1r i32, &'_#2r mut &'_#3r i32),
10+
(&'_#1r mut &'_#2r i32, &'_#3r i32),
1111
]
1212
= note: number of external vids: 4
13-
= note: where '_#1r: '_#3r
13+
= note: where '_#3r: '_#2r
1414

1515
note: external requirements
1616
--> $DIR/escape-upvar-nested.rs:20:27
@@ -25,10 +25,10 @@ LL | | };
2525
= note: defining type: test::{closure#0} with closure substs [
2626
i16,
2727
extern "rust-call" fn(()),
28-
(&'_#1r i32, &'_#2r mut &'_#3r i32),
28+
(&'_#1r mut &'_#2r i32, &'_#3r i32),
2929
]
3030
= note: number of external vids: 4
31-
= note: where '_#1r: '_#3r
31+
= note: where '_#3r: '_#2r
3232

3333
note: no external requirements
3434
--> $DIR/escape-upvar-nested.rs:13:1

‎src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ LL | let mut closure = || p = &y;
77
= note: defining type: test::{closure#0} with closure substs [
88
i16,
99
extern "rust-call" fn(()),
10-
(&'_#1r i32, &'_#2r mut &'_#3r i32),
10+
(&'_#1r mut &'_#2r i32, &'_#3r i32),
1111
]
1212
= note: number of external vids: 4
13-
= note: where '_#1r: '_#3r
13+
= note: where '_#3r: '_#2r
1414

1515
note: no external requirements
1616
--> $DIR/escape-upvar-ref.rs:17:1

0 commit comments

Comments
 (0)
Please sign in to comment.