Rollup merge of #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`
This commit is contained in:
Jonas Schievink 2021-02-02 12:14:44 +01:00 committed by GitHub
commit a1887912e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 812 additions and 53 deletions

View File

@ -2968,6 +2968,7 @@ declare_lint_pass! {
UNSUPPORTED_NAKED_FUNCTIONS, UNSUPPORTED_NAKED_FUNCTIONS,
MISSING_ABI, MISSING_ABI,
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
DISJOINT_CAPTURE_DROP_REORDER,
] ]
} }
@ -2994,6 +2995,51 @@ declare_lint! {
"detects doc comments that aren't used by rustdoc" "detects doc comments that aren't used by rustdoc"
} }
declare_lint! {
/// The `disjoint_capture_drop_reorder` lint detects variables that aren't completely
/// captured when the feature `capture_disjoint_fields` is enabled and it affects the Drop
/// order of at least one path starting at this variable.
///
/// ### Example
///
/// ```rust,compile_fail
/// # #![deny(disjoint_capture_drop_reorder)]
/// # #![allow(unused)]
/// struct FancyInteger(i32);
///
/// impl Drop for FancyInteger {
/// fn drop(&mut self) {
/// println!("Just dropped {}", self.0);
/// }
/// }
///
/// struct Point { x: FancyInteger, y: FancyInteger }
///
/// fn main() {
/// let p = Point { x: FancyInteger(10), y: FancyInteger(20) };
///
/// let c = || {
/// let x = p.x;
/// };
///
/// c();
///
/// // ... More code ...
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// In the above example `p.y` will be dropped at the end of `f` instead of with `c` if
/// the feature `capture_disjoint_fields` is enabled.
pub DISJOINT_CAPTURE_DROP_REORDER,
Allow,
"Drop reorder because of `capture_disjoint_fields`"
}
declare_lint_pass!(UnusedDocComment => [UNUSED_DOC_COMMENTS]); declare_lint_pass!(UnusedDocComment => [UNUSED_DOC_COMMENTS]);
declare_lint! { declare_lint! {

View File

@ -30,6 +30,7 @@
//! then mean that all later passes would have to check for these figments //! then mean that all later passes would have to check for these figments
//! and report an error, and it just seems like more mess in the end.) //! and report an error, and it just seems like more mess in the end.)
use super::writeback::Resolver;
use super::FnCtxt; use super::FnCtxt;
use crate::expr_use_visitor as euv; use crate::expr_use_visitor as euv;
@ -40,7 +41,9 @@ use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_infer::infer::UpvarRegion; use rustc_infer::infer::UpvarRegion;
use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, ProjectionKind}; use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, ProjectionKind};
use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults, UpvarSubsts};
use rustc_session::lint;
use rustc_span::sym; use rustc_span::sym;
use rustc_span::{MultiSpan, Span, Symbol}; use rustc_span::{MultiSpan, Span, Symbol};
@ -55,6 +58,11 @@ enum PlaceAncestryRelation {
Divergent, Divergent,
} }
/// Intermediate format to store a captured `Place` and associated `ty::CaptureInfo`
/// during capture analysis. Information in this map feeds into the minimum capture
/// analysis pass.
type InferredCaptureInformation<'tcx> = FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> { impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) {
InferBorrowKindVisitor { fcx: self }.visit_body(body); InferBorrowKindVisitor { fcx: self }.visit_body(body);
@ -92,7 +100,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self, &self,
closure_hir_id: hir::HirId, closure_hir_id: hir::HirId,
span: Span, span: Span,
body: &hir::Body<'_>, body: &'tcx hir::Body<'tcx>,
capture_clause: hir::CaptureBy, capture_clause: hir::CaptureBy,
) { ) {
debug!("analyze_closure(id={:?}, body.id={:?})", closure_hir_id, body.id()); debug!("analyze_closure(id={:?}, body.id={:?})", closure_hir_id, body.id());
@ -124,28 +132,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let local_def_id = closure_def_id.expect_local(); let local_def_id = closure_def_id.expect_local();
let mut capture_information: FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>> =
Default::default();
if !self.tcx.features().capture_disjoint_fields {
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
for (&var_hir_id, _) in upvars.iter() {
let place = self.place_for_root_variable(local_def_id, var_hir_id);
debug!("seed place {:?}", place);
let upvar_id = ty::UpvarId::new(var_hir_id, local_def_id);
let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span);
let info = ty::CaptureInfo {
capture_kind_expr_id: None,
path_expr_id: None,
capture_kind,
};
capture_information.insert(place, info);
}
}
}
let body_owner_def_id = self.tcx.hir().body_owner_def_id(body.id()); let body_owner_def_id = self.tcx.hir().body_owner_def_id(body.id());
assert_eq!(body_owner_def_id.to_def_id(), closure_def_id); assert_eq!(body_owner_def_id.to_def_id(), closure_def_id);
let mut delegate = InferBorrowKind { let mut delegate = InferBorrowKind {
@ -155,7 +141,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
capture_clause, capture_clause,
current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM, current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM,
current_origin: None, current_origin: None,
capture_information, capture_information: Default::default(),
}; };
euv::ExprUseVisitor::new( euv::ExprUseVisitor::new(
&mut delegate, &mut delegate,
@ -172,6 +158,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
); );
self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span); self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span);
self.compute_min_captures(closure_def_id, delegate.capture_information);
let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
if should_do_migration_analysis(self.tcx, closure_hir_id) {
self.perform_2229_migration_anaysis(closure_def_id, capture_clause, span, body);
}
// We now fake capture information for all variables that are mentioned within the closure
// We do this after handling migrations so that min_captures computes before
if !self.tcx.features().capture_disjoint_fields {
let mut capture_information: InferredCaptureInformation<'tcx> = Default::default();
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
for var_hir_id in upvars.keys() {
let place = self.place_for_root_variable(local_def_id, *var_hir_id);
debug!("seed place {:?}", place);
let upvar_id = ty::UpvarId::new(*var_hir_id, local_def_id);
let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span);
let fake_info = ty::CaptureInfo {
capture_kind_expr_id: None,
path_expr_id: None,
capture_kind,
};
capture_information.insert(place, fake_info);
}
}
// This will update the min captures based on this new fake information.
self.compute_min_captures(closure_def_id, capture_information);
}
if let Some(closure_substs) = infer_kind { if let Some(closure_substs) = infer_kind {
// Unify the (as yet unbound) type variable in the closure // Unify the (as yet unbound) type variable in the closure
// substs with the kind we inferred. // substs with the kind we inferred.
@ -197,7 +217,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
} }
self.compute_min_captures(closure_def_id, delegate);
self.log_closure_min_capture_info(closure_def_id, span); self.log_closure_min_capture_info(closure_def_id, span);
self.min_captures_to_closure_captures_bridge(closure_def_id); self.min_captures_to_closure_captures_bridge(closure_def_id);
@ -344,6 +363,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Places (and corresponding capture kind) that we need to keep track of to support all /// Places (and corresponding capture kind) that we need to keep track of to support all
/// the required captured paths. /// the required captured paths.
/// ///
///
/// Note: If this function is called multiple times for the same closure, it will update
/// the existing min_capture map that is stored in TypeckResults.
///
/// Eg: /// Eg:
/// ```rust,no_run /// ```rust,no_run
/// struct Point { x: i32, y: i32 } /// struct Point { x: i32, y: i32 }
@ -408,11 +431,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn compute_min_captures( fn compute_min_captures(
&self, &self,
closure_def_id: DefId, closure_def_id: DefId,
inferred_info: InferBorrowKind<'_, 'tcx>, capture_information: InferredCaptureInformation<'tcx>,
) { ) {
let mut root_var_min_capture_list: ty::RootVariableMinCaptureList<'_> = Default::default(); if capture_information.is_empty() {
return;
}
for (place, capture_info) in inferred_info.capture_information.into_iter() { let mut typeck_results = self.typeck_results.borrow_mut();
let mut root_var_min_capture_list =
typeck_results.closure_min_captures.remove(&closure_def_id).unwrap_or_default();
for (place, capture_info) in capture_information.into_iter() {
let var_hir_id = match place.base { let var_hir_id = match place.base {
PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
base => bug!("Expected upvar, found={:?}", base), base => bug!("Expected upvar, found={:?}", base),
@ -422,7 +452,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
None => { None => {
let mutability = self.determine_capture_mutability(&place); let mutability = self.determine_capture_mutability(&typeck_results, &place);
let min_cap_list = let min_cap_list =
vec![ty::CapturedPlace { place, info: capture_info, mutability }]; vec![ty::CapturedPlace { place, info: capture_info, mutability }];
root_var_min_capture_list.insert(var_hir_id, min_cap_list); root_var_min_capture_list.insert(var_hir_id, min_cap_list);
@ -487,7 +517,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Only need to insert when we don't have an ancestor in the existing min capture list // Only need to insert when we don't have an ancestor in the existing min capture list
if !ancestor_found { if !ancestor_found {
let mutability = self.determine_capture_mutability(&place); let mutability = self.determine_capture_mutability(&typeck_results, &place);
let captured_place = let captured_place =
ty::CapturedPlace { place, info: updated_capture_info, mutability }; ty::CapturedPlace { place, info: updated_capture_info, mutability };
min_cap_list.push(captured_place); min_cap_list.push(captured_place);
@ -495,15 +525,123 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list); debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list);
typeck_results.closure_min_captures.insert(closure_def_id, root_var_min_capture_list);
}
if !root_var_min_capture_list.is_empty() { /// Perform the migration analysis for RFC 2229, and emit lint
self.typeck_results /// `disjoint_capture_drop_reorder` if needed.
.borrow_mut() fn perform_2229_migration_anaysis(
.closure_min_captures &self,
.insert(closure_def_id, root_var_min_capture_list); closure_def_id: DefId,
capture_clause: hir::CaptureBy,
span: Span,
body: &'tcx hir::Body<'tcx>,
) {
let need_migrations = self.compute_2229_migrations_first_pass(
closure_def_id,
span,
capture_clause,
body,
self.typeck_results.borrow().closure_min_captures.get(&closure_def_id),
);
if !need_migrations.is_empty() {
let need_migrations_hir_id = need_migrations.iter().map(|m| m.0).collect::<Vec<_>>();
let migrations_text = migration_suggestion_for_2229(self.tcx, &need_migrations_hir_id);
let local_def_id = closure_def_id.expect_local();
let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
self.tcx.struct_span_lint_hir(
lint::builtin::DISJOINT_CAPTURE_DROP_REORDER,
closure_hir_id,
span,
|lint| {
let mut diagnostics_builder = lint.build(
"drop order affected for closure because of `capture_disjoint_fields`",
);
diagnostics_builder.note(&migrations_text);
diagnostics_builder.emit();
},
);
} }
} }
/// Figures out the list of root variables (and their types) that aren't completely
/// captured by the closure when `capture_disjoint_fields` is enabled and drop order of
/// some path starting at that root variable **might** be affected.
///
/// The output list would include a root variable if:
/// - It would have been moved into the closure when `capture_disjoint_fields` wasn't
/// enabled, **and**
/// - It wasn't completely captured by the closure, **and**
/// - The type of the root variable needs Drop.
fn compute_2229_migrations_first_pass(
&self,
closure_def_id: DefId,
closure_span: Span,
closure_clause: hir::CaptureBy,
body: &'tcx hir::Body<'tcx>,
min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>,
) -> Vec<(hir::HirId, Ty<'tcx>)> {
fn resolve_ty<T: TypeFoldable<'tcx>>(
fcx: &FnCtxt<'_, 'tcx>,
span: Span,
body: &'tcx hir::Body<'tcx>,
ty: T,
) -> T {
let mut resolver = Resolver::new(fcx, &span, body);
ty.fold_with(&mut resolver)
}
let upvars = if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
upvars
} else {
return vec![];
};
let mut need_migrations = Vec::new();
for (&var_hir_id, _) in upvars.iter() {
let ty = resolve_ty(self, closure_span, body, self.node_ty(var_hir_id));
if !ty.needs_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) {
continue;
}
let root_var_min_capture_list = if let Some(root_var_min_capture_list) =
min_captures.and_then(|m| m.get(&var_hir_id))
{
root_var_min_capture_list
} else {
// The upvar is mentioned within the closure but no path starting from it is
// used.
match closure_clause {
// Only migrate if closure is a move closure
hir::CaptureBy::Value => need_migrations.push((var_hir_id, ty)),
hir::CaptureBy::Ref => {}
}
continue;
};
let is_moved = root_var_min_capture_list
.iter()
.any(|capture| matches!(capture.info.capture_kind, ty::UpvarCapture::ByValue(_)));
let is_not_completely_captured =
root_var_min_capture_list.iter().any(|capture| capture.place.projections.len() > 0);
if is_moved && is_not_completely_captured {
need_migrations.push((var_hir_id, ty));
}
}
need_migrations
}
fn init_capture_kind( fn init_capture_kind(
&self, &self,
capture_clause: hir::CaptureBy, capture_clause: hir::CaptureBy,
@ -613,18 +751,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// A captured place is mutable if /// A captured place is mutable if
/// 1. Projections don't include a Deref of an immut-borrow, **and** /// 1. Projections don't include a Deref of an immut-borrow, **and**
/// 2. PlaceBase is mut or projections include a Deref of a mut-borrow. /// 2. PlaceBase is mut or projections include a Deref of a mut-borrow.
fn determine_capture_mutability(&self, place: &Place<'tcx>) -> hir::Mutability { fn determine_capture_mutability(
&self,
typeck_results: &'a TypeckResults<'tcx>,
place: &Place<'tcx>,
) -> hir::Mutability {
let var_hir_id = match place.base { let var_hir_id = match place.base {
PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
_ => unreachable!(), _ => unreachable!(),
}; };
let bm = *self let bm = *typeck_results.pat_binding_modes().get(var_hir_id).expect("missing binding mode");
.typeck_results
.borrow()
.pat_binding_modes()
.get(var_hir_id)
.expect("missing binding mode");
let mut is_mutbl = match bm { let mut is_mutbl = match bm {
ty::BindByValue(mutability) => mutability, ty::BindByValue(mutability) => mutability,
@ -698,9 +835,11 @@ struct InferBorrowKind<'a, 'tcx> {
/// ///
/// For closure `fix_s`, (at a high level) the map contains /// For closure `fix_s`, (at a high level) the map contains
/// ///
/// ```
/// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow } /// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow }
/// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow } /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
capture_information: FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>, /// ```
capture_information: InferredCaptureInformation<'tcx>,
} }
impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
@ -1119,6 +1258,21 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
tcx.hir().name(var_hir_id) tcx.hir().name(var_hir_id)
} }
fn should_do_migration_analysis(tcx: TyCtxt<'_>, closure_id: hir::HirId) -> bool {
let (level, _) =
tcx.lint_level_at_node(lint::builtin::DISJOINT_CAPTURE_DROP_REORDER, closure_id);
!matches!(level, lint::Level::Allow)
}
fn migration_suggestion_for_2229(tcx: TyCtxt<'_>, need_migrations: &Vec<hir::HirId>) -> String {
let need_migrations_strings =
need_migrations.iter().map(|v| format!("{}", var_name(tcx, *v))).collect::<Vec<_>>();
let migrations_list_concat = need_migrations_strings.join(", ");
format!("drop(&({}));", migrations_list_concat)
}
/// Helper function to determine if we need to escalate CaptureKind from /// Helper function to determine if we need to escalate CaptureKind from
/// CaptureInfo A to B and returns the escalated CaptureInfo. /// CaptureInfo A to B and returns the escalated CaptureInfo.
/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way) /// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)

View File

@ -650,7 +650,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
} }
} }
trait Locatable { crate trait Locatable {
fn to_span(&self, tcx: TyCtxt<'_>) -> Span; fn to_span(&self, tcx: TyCtxt<'_>) -> Span;
} }
@ -668,7 +668,7 @@ impl Locatable for hir::HirId {
/// The Resolver. This is the type folding engine that detects /// The Resolver. This is the type folding engine that detects
/// unresolved types and so forth. /// unresolved types and so forth.
struct Resolver<'cx, 'tcx> { crate struct Resolver<'cx, 'tcx> {
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
infcx: &'cx InferCtxt<'cx, 'tcx>, infcx: &'cx InferCtxt<'cx, 'tcx>,
span: &'cx dyn Locatable, span: &'cx dyn Locatable,
@ -679,7 +679,7 @@ struct Resolver<'cx, 'tcx> {
} }
impl<'cx, 'tcx> Resolver<'cx, 'tcx> { impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
fn new( crate fn new(
fcx: &'cx FnCtxt<'cx, 'tcx>, fcx: &'cx FnCtxt<'cx, 'tcx>,
span: &'cx dyn Locatable, span: &'cx dyn Locatable,
body: &'tcx hir::Body<'tcx>, body: &'tcx hir::Body<'tcx>,

View File

@ -0,0 +1,130 @@
#![deny(disjoint_capture_drop_reorder)]
//~^ NOTE: the lint level is defined here
// Test cases for types that implement a insignificant drop (stlib defined)
// `t` needs Drop because one of its elements needs drop,
// therefore precise capture might affect drop ordering
fn test1_all_need_migration() {
let t = (String::new(), String::new());
let t1 = (String::new(), String::new());
let t2 = (String::new(), String::new());
let c = || {
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
//~| NOTE: drop(&(t, t1, t2));
let _t = t.0;
let _t1 = t1.0;
let _t2 = t2.0;
};
c();
}
// String implements drop and therefore should be migrated.
// But in this test cases, `t2` is completely captured and when it is dropped won't be affected
fn test2_only_precise_paths_need_migration() {
let t = (String::new(), String::new());
let t1 = (String::new(), String::new());
let t2 = (String::new(), String::new());
let c = || {
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
//~| NOTE: drop(&(t, t1));
let _t = t.0;
let _t1 = t1.0;
let _t2 = t2;
};
c();
}
// If a variable would've not been captured by value then it would've not been
// dropped with the closure and therefore doesn't need migration.
fn test3_only_by_value_need_migration() {
let t = (String::new(), String::new());
let t1 = (String::new(), String::new());
let c = || {
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
//~| NOTE: drop(&(t));
let _t = t.0;
println!("{}", t1.1);
};
c();
}
// Copy types get copied into the closure instead of move. Therefore we don't need to
// migrate then as their drop order isn't tied to the closure.
fn test4_only_non_copy_types_need_migration() {
let t = (String::new(), String::new());
// `t1` is Copy because all of its elements are Copy
let t1 = (0i32, 0i32);
let c = || {
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
//~| NOTE: drop(&(t));
let _t = t.0;
let _t1 = t1.0;
};
c();
}
fn test5_only_drop_types_need_migration() {
struct S(i32, i32);
let t = (String::new(), String::new());
// `s` doesn't implement Drop or any elements within it, and doesn't need migration
let s = S(0i32, 0i32);
let c = || {
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
//~| NOTE: drop(&(t));
let _t = t.0;
let _s = s.0;
};
c();
}
// Since we are using a move closure here, both `t` and `t1` get moved
// even though they are being used by ref inside the closure.
fn test6_move_closures_non_copy_types_might_need_migration() {
let t = (String::new(), String::new());
let t1 = (String::new(), String::new());
let c = move || {
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
//~| NOTE: drop(&(t1, t));
println!("{} {}", t1.1, t.1);
};
c();
}
// Test migration analysis in case of Drop + Non Drop aggregates.
// Note we need migration here only because the non-copy (because Drop type) is captured,
// otherwise we won't need to, since we can get away with just by ref capture in that case.
fn test7_drop_non_drop_aggregate_need_migration() {
let t = (String::new(), String::new(), 0i32);
let c = || {
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
//~| NOTE: drop(&(t));
let _t = t.0;
};
c();
}
fn main() {
test1_all_need_migration();
test2_only_precise_paths_need_migration();
test3_only_by_value_need_migration();
test4_only_non_copy_types_need_migration();
test5_only_drop_types_need_migration();
test6_move_closures_non_copy_types_might_need_migration();
test7_drop_non_drop_aggregate_need_migration();
}

View File

@ -0,0 +1,105 @@
error: drop order affected for closure because of `capture_disjoint_fields`
--> $DIR/insignificant_drop.rs:13:13
|
LL | let c = || {
| _____________^
LL | |
LL | |
LL | | let _t = t.0;
LL | | let _t1 = t1.0;
LL | | let _t2 = t2.0;
LL | | };
| |_____^
|
note: the lint level is defined here
--> $DIR/insignificant_drop.rs:1:9
|
LL | #![deny(disjoint_capture_drop_reorder)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: drop(&(t, t1, t2));
error: drop order affected for closure because of `capture_disjoint_fields`
--> $DIR/insignificant_drop.rs:31:13
|
LL | let c = || {
| _____________^
LL | |
LL | |
LL | | let _t = t.0;
LL | | let _t1 = t1.0;
LL | | let _t2 = t2;
LL | | };
| |_____^
|
= note: drop(&(t, t1));
error: drop order affected for closure because of `capture_disjoint_fields`
--> $DIR/insignificant_drop.rs:47:13
|
LL | let c = || {
| _____________^
LL | |
LL | |
LL | | let _t = t.0;
LL | | println!("{}", t1.1);
LL | | };
| |_____^
|
= note: drop(&(t));
error: drop order affected for closure because of `capture_disjoint_fields`
--> $DIR/insignificant_drop.rs:65:13
|
LL | let c = || {
| _____________^
LL | |
LL | |
LL | | let _t = t.0;
LL | | let _t1 = t1.0;
LL | | };
| |_____^
|
= note: drop(&(t));
error: drop order affected for closure because of `capture_disjoint_fields`
--> $DIR/insignificant_drop.rs:83:13
|
LL | let c = || {
| _____________^
LL | |
LL | |
LL | | let _t = t.0;
LL | | let _s = s.0;
LL | | };
| |_____^
|
= note: drop(&(t));
error: drop order affected for closure because of `capture_disjoint_fields`
--> $DIR/insignificant_drop.rs:98:13
|
LL | let c = move || {
| _____________^
LL | |
LL | |
LL | | println!("{} {}", t1.1, t.1);
LL | | };
| |_____^
|
= note: drop(&(t1, t));
error: drop order affected for closure because of `capture_disjoint_fields`
--> $DIR/insignificant_drop.rs:113:13
|
LL | let c = || {
| _____________^
LL | |
LL | |
LL | | let _t = t.0;
LL | | };
| |_____^
|
= note: drop(&(t));
error: aborting due to 7 previous errors

View File

@ -0,0 +1,84 @@
// run-pass
// Set of test cases that don't need migrations
#![deny(disjoint_capture_drop_reorder)]
// Copy types as copied by the closure instead of being moved into the closure
// Therefore their drop order isn't tied to the closure and won't be requiring any
// migrations.
fn test1_only_copy_types() {
let t = (0i32, 0i32);
let c = || {
let _t = t.0;
};
c();
}
// Same as test1 but using a move closure
fn test2_only_copy_types_move_closure() {
let t = (0i32, 0i32);
let c = move || {
println!("{}", t.0);
};
c();
}
// Don't need to migrate if captured by ref
fn test3_only_copy_types_move_closure() {
let t = (String::new(), String::new());
let c = || {
println!("{}", t.0);
};
c();
}
// Test migration analysis in case of Insignificant Drop + Non Drop aggregates.
// Note in this test the closure captures a non Drop type and therefore the variable
// is only captured by ref.
fn test4_insignificant_drop_non_drop_aggregate() {
let t = (String::new(), 0i32);
let c = || {
let _t = t.1;
};
c();
}
struct Foo(i32);
impl Drop for Foo {
fn drop(&mut self) {
println!("{:?} dropped", self.0);
}
}
// Test migration analysis in case of Significant Drop + Non Drop aggregates.
// Note in this test the closure captures a non Drop type and therefore the variable
// is only captured by ref.
fn test5_significant_drop_non_drop_aggregate() {
let t = (Foo(0), 0i32);
let c = || {
let _t = t.1;
};
c();
}
fn main() {
test1_only_copy_types();
test2_only_copy_types_move_closure();
test3_only_copy_types_move_closure();
test4_insignificant_drop_non_drop_aggregate();
test5_significant_drop_non_drop_aggregate();
}

View File

@ -0,0 +1,137 @@
#![deny(disjoint_capture_drop_reorder)]
//~^ NOTE: the lint level is defined here
// Test cases for types that implement a significant drop (user defined)
#[derive(Debug)]
struct Foo(i32);
impl Drop for Foo {
fn drop(&mut self) {
println!("{:?} dropped", self.0);
}
}
#[derive(Debug)]
struct ConstainsDropField(Foo, Foo);
// `t` needs Drop because one of its elements needs drop,
// therefore precise capture might affect drop ordering
fn test1_all_need_migration() {
let t = (Foo(0), Foo(0));
let t1 = (Foo(0), Foo(0));
let t2 = (Foo(0), Foo(0));
let c = || {
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
//~| NOTE: drop(&(t, t1, t2));
let _t = t.0;
let _t1 = t1.0;
let _t2 = t2.0;
};
c();
}
// String implements drop and therefore should be migrated.
// But in this test cases, `t2` is completely captured and when it is dropped won't be affected
fn test2_only_precise_paths_need_migration() {
let t = (Foo(0), Foo(0));
let t1 = (Foo(0), Foo(0));
let t2 = (Foo(0), Foo(0));
let c = || {
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
//~| NOTE: drop(&(t, t1));
let _t = t.0;
let _t1 = t1.0;
let _t2 = t2;
};
c();
}
// If a variable would've not been captured by value then it would've not been
// dropped with the closure and therefore doesn't need migration.
fn test3_only_by_value_need_migration() {
let t = (Foo(0), Foo(0));
let t1 = (Foo(0), Foo(0));
let c = || {
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
//~| NOTE: drop(&(t));
let _t = t.0;
println!("{:?}", t1.1);
};
c();
}
// The root variable might not implement drop themselves but some path starting
// at the root variable might implement Drop.
//
// If this path isn't captured we need to migrate for the root variable.
fn test4_type_contains_drop_need_migration() {
let t = ConstainsDropField(Foo(0), Foo(0));
let c = || {
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
//~| NOTE: drop(&(t));
let _t = t.0;
};
c();
}
// Test migration analysis in case of Drop + Non Drop aggregates.
// Note we need migration here only because the non-copy (because Drop type) is captured,
// otherwise we won't need to, since we can get away with just by ref capture in that case.
fn test5_drop_non_drop_aggregate_need_migration() {
let t = (Foo(0), Foo(0), 0i32);
let c = || {
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
//~| NOTE: drop(&(t));
let _t = t.0;
};
c();
}
// Test migration analysis in case of Significant and Insignificant Drop aggregates.
fn test6_significant_insignificant_drop_aggregate_need_migration() {
struct S(i32, i32);
let t = (Foo(0), String::new());
let c = || {
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
//~| NOTE: drop(&(t));
let _t = t.1;
};
c();
}
// Since we are using a move closure here, both `t` and `t1` get moved
// even though they are being used by ref inside the closure.
fn test7_move_closures_non_copy_types_might_need_migration() {
let t = (Foo(0), Foo(0));
let t1 = (Foo(0), Foo(0), Foo(0));
let c = move || {
//~^ERROR: drop order affected for closure because of `capture_disjoint_fields`
//~| NOTE: drop(&(t1, t));
println!("{:?} {:?}", t1.1, t.1);
};
c();
}
fn main() {
test1_all_need_migration();
test2_only_precise_paths_need_migration();
test3_only_by_value_need_migration();
test4_type_contains_drop_need_migration();
test5_drop_non_drop_aggregate_need_migration();
test6_significant_insignificant_drop_aggregate_need_migration();
test7_move_closures_non_copy_types_might_need_migration();
}

View File

@ -0,0 +1,103 @@
error: drop order affected for closure because of `capture_disjoint_fields`
--> $DIR/significant_drop.rs:24:13
|
LL | let c = || {
| _____________^
LL | |
LL | |
LL | | let _t = t.0;
LL | | let _t1 = t1.0;
LL | | let _t2 = t2.0;
LL | | };
| |_____^
|
note: the lint level is defined here
--> $DIR/significant_drop.rs:1:9
|
LL | #![deny(disjoint_capture_drop_reorder)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: drop(&(t, t1, t2));
error: drop order affected for closure because of `capture_disjoint_fields`
--> $DIR/significant_drop.rs:42:13
|
LL | let c = || {
| _____________^
LL | |
LL | |
LL | | let _t = t.0;
LL | | let _t1 = t1.0;
LL | | let _t2 = t2;
LL | | };
| |_____^
|
= note: drop(&(t, t1));
error: drop order affected for closure because of `capture_disjoint_fields`
--> $DIR/significant_drop.rs:58:13
|
LL | let c = || {
| _____________^
LL | |
LL | |
LL | | let _t = t.0;
LL | | println!("{:?}", t1.1);
LL | | };
| |_____^
|
= note: drop(&(t));
error: drop order affected for closure because of `capture_disjoint_fields`
--> $DIR/significant_drop.rs:75:13
|
LL | let c = || {
| _____________^
LL | |
LL | |
LL | | let _t = t.0;
LL | | };
| |_____^
|
= note: drop(&(t));
error: drop order affected for closure because of `capture_disjoint_fields`
--> $DIR/significant_drop.rs:90:13
|
LL | let c = || {
| _____________^
LL | |
LL | |
LL | | let _t = t.0;
LL | | };
| |_____^
|
= note: drop(&(t));
error: drop order affected for closure because of `capture_disjoint_fields`
--> $DIR/significant_drop.rs:105:13
|
LL | let c = || {
| _____________^
LL | |
LL | |
LL | | let _t = t.1;
LL | | };
| |_____^
|
= note: drop(&(t));
error: drop order affected for closure because of `capture_disjoint_fields`
--> $DIR/significant_drop.rs:120:13
|
LL | let c = move || {
| _____________^
LL | |
LL | |
LL | | println!("{:?} {:?}", t1.1, t.1);
LL | | };
| |_____^
|
= note: drop(&(t1, t));
error: aborting due to 7 previous errors

View File

@ -7,10 +7,10 @@ LL | let mut closure1 = || p = &y;
= note: defining type: test::{closure#0}::{closure#0} with closure substs [ = note: defining type: test::{closure#0}::{closure#0} with closure substs [
i16, i16,
extern "rust-call" fn(()), extern "rust-call" fn(()),
(&'_#1r i32, &'_#2r mut &'_#3r i32), (&'_#1r mut &'_#2r i32, &'_#3r i32),
] ]
= note: number of external vids: 4 = note: number of external vids: 4
= note: where '_#1r: '_#3r = note: where '_#3r: '_#2r
note: external requirements note: external requirements
--> $DIR/escape-upvar-nested.rs:20:27 --> $DIR/escape-upvar-nested.rs:20:27
@ -25,10 +25,10 @@ LL | | };
= note: defining type: test::{closure#0} with closure substs [ = note: defining type: test::{closure#0} with closure substs [
i16, i16,
extern "rust-call" fn(()), extern "rust-call" fn(()),
(&'_#1r i32, &'_#2r mut &'_#3r i32), (&'_#1r mut &'_#2r i32, &'_#3r i32),
] ]
= note: number of external vids: 4 = note: number of external vids: 4
= note: where '_#1r: '_#3r = note: where '_#3r: '_#2r
note: no external requirements note: no external requirements
--> $DIR/escape-upvar-nested.rs:13:1 --> $DIR/escape-upvar-nested.rs:13:1

View File

@ -7,10 +7,10 @@ LL | let mut closure = || p = &y;
= note: defining type: test::{closure#0} with closure substs [ = note: defining type: test::{closure#0} with closure substs [
i16, i16,
extern "rust-call" fn(()), extern "rust-call" fn(()),
(&'_#1r i32, &'_#2r mut &'_#3r i32), (&'_#1r mut &'_#2r i32, &'_#3r i32),
] ]
= note: number of external vids: 4 = note: number of external vids: 4
= note: where '_#1r: '_#3r = note: where '_#3r: '_#2r
note: no external requirements note: no external requirements
--> $DIR/escape-upvar-ref.rs:17:1 --> $DIR/escape-upvar-ref.rs:17:1