2015-01-02 09:12:25 +00:00
//! ### Inferring borrow kinds for upvars
//!
//! Whenever there is a closure expression, we need to determine how each
//! upvar is used. We do this by initially assigning each upvar an
//! immutable "borrow kind" (see `ty::BorrowKind` for details) and then
//! "escalating" the kind as needed. The borrow kind proceeds according to
//! the following lattice:
2022-04-15 22:04:34 +00:00
//! ```ignore (not-rust)
//! ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow
//! ```
2015-01-02 09:12:25 +00:00
//! So, for example, if we see an assignment `x = 5` to an upvar `x`, we
//! will promote its borrow kind to mutable borrow. If we see an `&mut x`
//! we'll do the same. Naturally, this applies not just to the upvar, but
//! to everything owned by `x`, so the result is the same for something
//! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a
//! struct). These adjustments are performed in
//! `adjust_upvar_borrow_kind()` (you can trace backwards through the code
//! from there).
//!
//! The fact that we are inferring borrow kinds as we go results in a
//! semi-hacky interaction with mem-categorization. In particular,
//! mem-categorization will query the current borrow kind as it
//! categorizes, and we'll return the *current* value, but this may get
//! adjusted later. Therefore, in this module, we generally ignore the
//! borrow kind (and derived mutabilities) that are returned from
//! mem-categorization, since they may be inaccurate. (Another option
//! would be to use a unification scheme, where instead of returning a
//! concrete borrow kind like `ty::ImmBorrow`, we return a
//! `ty::InferBorrow(upvar_id)` or something like that, but this would
//! 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.)
use super ::FnCtxt ;
2019-11-09 10:38:06 +00:00
use crate ::expr_use_visitor as euv ;
2022-03-24 02:03:04 +00:00
use rustc_errors ::{ Applicability , MultiSpan } ;
2020-01-05 01:37:57 +00:00
use rustc_hir as hir ;
use rustc_hir ::def_id ::LocalDefId ;
2021-11-03 23:03:12 +00:00
use rustc_hir ::intravisit ::{ self , Visitor } ;
2020-01-06 22:12:31 +00:00
use rustc_infer ::infer ::UpvarRegion ;
2020-12-27 06:13:51 +00:00
use rustc_middle ::hir ::place ::{ Place , PlaceBase , PlaceWithHirId , Projection , ProjectionKind } ;
2021-02-23 22:55:36 +00:00
use rustc_middle ::mir ::FakeReadCause ;
2021-07-03 19:29:09 +00:00
use rustc_middle ::ty ::{
self , ClosureSizeProfileData , Ty , TyCtxt , TypeckResults , UpvarCapture , UpvarSubsts ,
} ;
2020-12-15 08:38:15 +00:00
use rustc_session ::lint ;
2020-10-11 04:14:11 +00:00
use rustc_span ::sym ;
2022-03-24 02:03:04 +00:00
use rustc_span ::{ BytePos , Pos , Span , Symbol } ;
2021-07-06 09:38:15 +00:00
use rustc_trait_selection ::infer ::InferCtxtExt ;
2015-01-02 09:12:25 +00:00
2022-07-08 16:06:18 +00:00
use rustc_data_structures ::fx ::{ FxHashMap , FxHashSet } ;
2020-12-27 06:13:51 +00:00
use rustc_index ::vec ::Idx ;
use rustc_target ::abi ::VariantIdx ;
2021-03-08 23:32:41 +00:00
use std ::iter ;
2020-09-26 21:07:00 +00:00
/// Describe the relationship between the paths of two places
/// eg:
2020-11-09 05:15:45 +00:00
/// - `foo` is ancestor of `foo.bar.baz`
/// - `foo.bar.baz` is an descendant of `foo.bar`
/// - `foo.bar` and `foo.baz` are divergent
2020-09-26 21:07:00 +00:00
enum PlaceAncestryRelation {
Ancestor ,
Descendant ,
2021-10-09 05:41:05 +00:00
SamePlace ,
2020-09-26 21:07:00 +00:00
Divergent ,
}
2020-11-21 09:23:42 +00:00
/// 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.
2021-10-13 21:14:37 +00:00
type InferredCaptureInformation < ' tcx > = Vec < ( Place < ' tcx > , ty ::CaptureInfo ) > ;
2020-11-21 09:23:42 +00:00
2019-06-13 21:48:52 +00:00
impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
2019-11-29 10:09:23 +00:00
pub fn closure_analyze ( & self , body : & ' tcx hir ::Body < ' tcx > ) {
2017-06-08 10:38:30 +00:00
InferBorrowKindVisitor { fcx : self } . visit_body ( body ) ;
2015-08-31 19:37:05 +00:00
2016-05-11 05:48:12 +00:00
// it's our job to process these.
assert! ( self . deferred_call_resolutions . borrow ( ) . is_empty ( ) ) ;
}
2015-01-02 09:12:25 +00:00
}
2021-07-08 21:04:58 +00:00
/// Intermediate format to store the hir_id pointing to the use that resulted in the
/// corresponding place being captured and a String which contains the captured value's
/// name (i.e: a.b.c)
2021-11-04 16:50:24 +00:00
#[ derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash) ]
2021-11-05 16:43:42 +00:00
enum UpvarMigrationInfo {
2021-11-04 21:44:29 +00:00
/// We previously captured all of `x`, but now we capture some sub-path.
2021-11-05 16:43:42 +00:00
CapturingPrecise { source_expr : Option < hir ::HirId > , var_name : String } ,
2021-11-05 01:26:47 +00:00
CapturingNothing {
// where the variable appears in the closure (but is not captured)
use_span : Span ,
} ,
2021-11-04 16:50:24 +00:00
}
2021-07-08 21:04:58 +00:00
2021-11-04 21:44:29 +00:00
/// Reasons that we might issue a migration warning.
#[ derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash) ]
struct MigrationWarningReason {
/// When we used to capture `x` in its entirety, we implemented the auto-trait(s)
/// in this vec, but now we don't.
auto_traits : Vec < & 'static str > ,
/// When we used to capture `x` in its entirety, we would execute some destructors
/// at a different time.
drop_order : bool ,
}
impl MigrationWarningReason {
fn migration_message ( & self ) -> String {
let base = " changes to closure capture in Rust 2021 will affect " ;
if ! self . auto_traits . is_empty ( ) & & self . drop_order {
format! ( " {} drop order and which traits the closure implements " , base )
} else if self . drop_order {
format! ( " {} drop order " , base )
} else {
format! ( " {} which traits the closure implements " , base )
}
}
}
/// Intermediate format to store information needed to generate a note in the migration lint.
struct MigrationLintNote {
2021-11-05 16:43:42 +00:00
captures_info : UpvarMigrationInfo ,
2021-11-04 21:44:29 +00:00
/// reasons why migration is needed for this capture
reason : MigrationWarningReason ,
}
2021-07-08 21:04:58 +00:00
/// Intermediate format to store the hir id of the root variable and a HashSet containing
/// information on why the root variable should be fully captured
2021-11-04 21:44:29 +00:00
struct NeededMigration {
var_hir_id : hir ::HirId ,
diagnostics_info : Vec < MigrationLintNote > ,
}
2021-07-08 21:04:58 +00:00
2019-06-13 21:48:52 +00:00
struct InferBorrowKindVisitor < ' a , ' tcx > {
fcx : & ' a FnCtxt < ' a , ' tcx > ,
2015-01-02 09:12:25 +00:00
}
2019-06-13 21:48:52 +00:00
impl < ' a , ' tcx > Visitor < ' tcx > for InferBorrowKindVisitor < ' a , ' tcx > {
2019-11-30 14:08:22 +00:00
fn visit_expr ( & mut self , expr : & ' tcx hir ::Expr < ' tcx > ) {
2021-10-23 23:26:40 +00:00
match expr . kind {
2022-07-11 19:39:53 +00:00
hir ::ExprKind ::Closure ( & hir ::Closure { capture_clause , body : body_id , .. } ) = > {
2021-10-23 23:26:40 +00:00
let body = self . fcx . tcx . hir ( ) . body ( body_id ) ;
self . visit_body ( body ) ;
2022-06-11 19:25:25 +00:00
self . fcx . analyze_closure ( expr . hir_id , expr . span , body_id , body , capture_clause ) ;
2021-10-23 23:26:40 +00:00
}
hir ::ExprKind ::ConstBlock ( anon_const ) = > {
let body = self . fcx . tcx . hir ( ) . body ( anon_const . body ) ;
self . visit_body ( body ) ;
}
_ = > { }
2015-01-02 09:12:25 +00:00
}
2015-11-17 22:51:44 +00:00
intravisit ::walk_expr ( self , expr ) ;
2015-01-02 09:12:25 +00:00
}
}
2019-06-13 21:48:52 +00:00
impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
2019-11-08 22:54:00 +00:00
/// Analysis starting point.
2021-08-20 13:36:04 +00:00
#[ instrument(skip(self, body), level = " debug " ) ]
2017-11-08 10:16:40 +00:00
fn analyze_closure (
& self ,
closure_hir_id : hir ::HirId ,
span : Span ,
2021-03-29 23:52:59 +00:00
body_id : hir ::BodyId ,
2020-12-15 08:38:15 +00:00
body : & ' tcx hir ::Body < ' tcx > ,
2019-11-09 17:23:11 +00:00
capture_clause : hir ::CaptureBy ,
2017-11-08 10:16:40 +00:00
) {
2017-11-08 17:36:28 +00:00
// Extract the type of the closure.
2018-12-07 17:14:30 +00:00
let ty = self . node_ty ( closure_hir_id ) ;
2020-08-02 22:49:11 +00:00
let ( closure_def_id , substs ) = match * ty . kind ( ) {
2019-09-26 17:30:44 +00:00
ty ::Closure ( def_id , substs ) = > ( def_id , UpvarSubsts ::Closure ( substs ) ) ,
2018-08-22 00:35:02 +00:00
ty ::Generator ( def_id , substs , _ ) = > ( def_id , UpvarSubsts ::Generator ( substs ) ) ,
2020-05-06 04:02:09 +00:00
ty ::Error ( _ ) = > {
2018-06-22 22:35:52 +00:00
// #51714: skip analysis when we have already encountered type errors
return ;
}
2018-12-07 17:14:30 +00:00
_ = > {
2017-11-08 17:36:28 +00:00
span_bug! (
span ,
" type of closure expr {:?} is not a closure {:?} " ,
2019-02-24 08:33:17 +00:00
closure_hir_id ,
2018-12-07 17:14:30 +00:00
ty
2017-11-08 17:36:28 +00:00
) ;
2017-06-08 10:38:30 +00:00
}
} ;
2022-07-12 14:10:22 +00:00
let closure_def_id = closure_def_id . expect_local ( ) ;
2017-06-08 10:38:30 +00:00
2018-11-14 16:41:16 +00:00
let infer_kind = if let UpvarSubsts ::Closure ( closure_substs ) = substs {
2020-03-13 01:23:38 +00:00
self . closure_kind ( closure_substs ) . is_none ( ) . then_some ( closure_substs )
2017-11-08 17:36:28 +00:00
} else {
2018-05-02 11:14:30 +00:00
None
2017-11-08 17:36:28 +00:00
} ;
2017-08-08 12:34:37 +00:00
2022-07-12 14:10:22 +00:00
assert_eq! ( self . tcx . hir ( ) . body_owner_def_id ( body . id ( ) ) , closure_def_id ) ;
2017-11-08 14:45:48 +00:00
let mut delegate = InferBorrowKind {
fcx : self ,
2022-07-12 14:10:22 +00:00
closure_def_id ,
2020-11-21 09:23:42 +00:00
capture_information : Default ::default ( ) ,
2021-02-03 02:07:52 +00:00
fake_reads : Default ::default ( ) ,
2017-11-08 14:45:48 +00:00
} ;
2019-11-08 22:11:03 +00:00
euv ::ExprUseVisitor ::new (
2017-11-08 14:45:48 +00:00
& mut delegate ,
& self . infcx ,
2022-07-12 14:10:22 +00:00
closure_def_id ,
2017-11-08 14:45:48 +00:00
self . param_env ,
2020-07-17 08:47:04 +00:00
& self . typeck_results . borrow ( ) ,
2018-12-17 01:01:57 +00:00
)
. consume_body ( body ) ;
2017-11-08 14:45:48 +00:00
2020-10-28 03:41:52 +00:00
debug! (
" For closure={:?}, capture_information={:#?} " ,
closure_def_id , delegate . capture_information
2020-10-13 00:16:02 +00:00
) ;
2021-07-09 04:25:41 +00:00
2020-11-13 06:51:19 +00:00
self . log_capture_analysis_first_pass ( closure_def_id , & delegate . capture_information , span ) ;
2020-10-13 00:16:02 +00:00
2021-07-09 04:25:41 +00:00
let ( capture_information , closure_kind , origin ) = self
. process_collected_capture_information ( capture_clause , delegate . capture_information ) ;
2021-10-13 20:20:10 +00:00
self . compute_min_captures ( closure_def_id , capture_information , span ) ;
2020-11-21 09:23:42 +00:00
2022-07-12 14:10:22 +00:00
let closure_hir_id = self . tcx . hir ( ) . local_def_id_to_hir_id ( closure_def_id ) ;
2021-04-08 23:09:08 +00:00
2021-06-29 13:35:15 +00:00
if should_do_rust_2021_incompatible_closure_captures_analysis ( self . tcx , closure_hir_id ) {
2021-03-29 23:52:59 +00:00
self . perform_2229_migration_anaysis ( closure_def_id , body_id , capture_clause , span ) ;
2020-12-15 08:38:15 +00:00
}
2021-05-05 19:57:08 +00:00
let after_feature_tys = self . final_upvar_tys ( closure_def_id ) ;
2020-11-21 09:23:42 +00:00
// 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
2021-06-21 06:48:18 +00:00
if ! enable_precise_capture ( self . tcx , span ) {
2020-11-21 09:23:42 +00:00
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 ( ) {
2022-07-12 14:10:22 +00:00
let place = self . place_for_root_variable ( closure_def_id , * var_hir_id ) ;
2020-11-21 09:23:42 +00:00
debug! ( " seed place {:?} " , place ) ;
2021-10-13 20:20:10 +00:00
let capture_kind = self . init_capture_kind_for_place ( & place , capture_clause ) ;
2020-11-21 09:23:42 +00:00
let fake_info = ty ::CaptureInfo {
capture_kind_expr_id : None ,
path_expr_id : None ,
capture_kind ,
} ;
2021-10-13 21:14:37 +00:00
capture_information . push ( ( place , fake_info ) ) ;
2020-11-21 09:23:42 +00:00
}
}
// This will update the min captures based on this new fake information.
2021-10-13 20:20:10 +00:00
self . compute_min_captures ( closure_def_id , capture_information , span ) ;
2020-11-21 09:23:42 +00:00
}
2021-05-05 19:57:08 +00:00
let before_feature_tys = self . final_upvar_tys ( closure_def_id ) ;
2018-05-02 11:14:30 +00:00
if let Some ( closure_substs ) = infer_kind {
2017-11-08 17:36:28 +00:00
// Unify the (as yet unbound) type variable in the closure
// substs with the kind we inferred.
2020-03-13 01:23:38 +00:00
let closure_kind_ty = closure_substs . as_closure ( ) . kind_ty ( ) ;
2021-07-09 04:25:41 +00:00
self . demand_eqtype ( span , closure_kind . to_ty ( self . tcx ) , closure_kind_ty ) ;
2017-11-08 17:36:28 +00:00
// If we have an origin, store it.
2021-07-09 04:25:41 +00:00
if let Some ( origin ) = origin {
2021-06-21 06:48:18 +00:00
let origin = if enable_precise_capture ( self . tcx , span ) {
2021-07-09 04:25:41 +00:00
( origin . 0 , origin . 1 )
2020-12-03 03:25:22 +00:00
} else {
( origin . 0 , Place { projections : vec ! [ ] , .. origin . 1 } )
} ;
2020-07-17 08:47:04 +00:00
self . typeck_results
. borrow_mut ( )
. closure_kind_origins_mut ( )
. insert ( closure_hir_id , origin ) ;
2017-06-08 10:38:30 +00:00
}
2015-07-17 12:22:03 +00:00
}
2015-01-02 09:12:25 +00:00
2020-10-28 03:41:52 +00:00
self . log_closure_min_capture_info ( closure_def_id , span ) ;
2015-07-17 12:22:03 +00:00
// Now that we've analyzed the closure, we know how each
// variable is borrowed, and we know what traits the closure
// implements (Fn vs FnMut etc). We now have some updates to do
// with that information.
2015-01-31 11:29:04 +00:00
//
2015-07-17 12:22:03 +00:00
// Note that no closure type C may have an upvar of type C
// (though it may reference itself via a trait object). This
// results from the desugaring of closures to a struct like
// `Foo<..., UV0...UVn>`. If one of those upvars referenced
// C, then the type would have infinite size (and the
// inference algorithm will reject it).
2017-11-08 14:45:48 +00:00
// Equate the type variables for the upvars with the actual types.
2020-11-26 04:56:55 +00:00
let final_upvar_tys = self . final_upvar_tys ( closure_def_id ) ;
2017-11-08 10:16:40 +00:00
debug! (
2018-05-02 11:14:30 +00:00
" analyze_closure: id={:?} substs={:?} final_upvar_tys={:?} " ,
2019-03-04 08:00:30 +00:00
closure_hir_id , substs , final_upvar_tys
2017-11-08 10:16:40 +00:00
) ;
2020-07-19 21:26:51 +00:00
// Build a tuple (U0..Un) of the final upvar types U0..Un
2022-03-30 19:14:15 +00:00
// and unify the upvar tuple type in the closure with it:
2020-07-19 21:26:51 +00:00
let final_tupled_upvars_type = self . tcx . mk_tup ( final_upvar_tys . iter ( ) ) ;
self . demand_suptype ( span , substs . tupled_upvars_ty ( ) , final_tupled_upvars_type ) ;
2015-07-17 12:22:03 +00:00
2021-02-25 23:03:41 +00:00
let fake_reads = delegate
. fake_reads
. into_iter ( )
. map ( | ( place , cause , hir_id ) | ( place , cause , hir_id ) )
. collect ( ) ;
2021-02-03 02:07:52 +00:00
self . typeck_results . borrow_mut ( ) . closure_fake_reads . insert ( closure_def_id , fake_reads ) ;
2022-07-06 12:44:47 +00:00
if self . tcx . sess . opts . unstable_opts . profile_closures {
2021-05-05 19:57:08 +00:00
self . typeck_results . borrow_mut ( ) . closure_size_eval . insert (
closure_def_id ,
ClosureSizeProfileData {
before_feature_tys : self . tcx . mk_tup ( before_feature_tys . into_iter ( ) ) ,
after_feature_tys : self . tcx . mk_tup ( after_feature_tys . into_iter ( ) ) ,
} ,
) ;
}
2017-06-08 10:38:30 +00:00
// If we are also inferred the closure kind here,
// process any deferred resolutions.
2017-11-08 17:36:28 +00:00
let deferred_call_resolutions = self . remove_deferred_call_resolutions ( closure_def_id ) ;
for deferred_call_resolution in deferred_call_resolutions {
deferred_call_resolution . resolve ( self ) ;
2015-01-31 11:29:04 +00:00
}
2015-01-02 09:12:25 +00:00
}
2020-01-09 02:47:32 +00:00
// Returns a list of `Ty`s for each upvar.
2022-07-12 14:10:22 +00:00
fn final_upvar_tys ( & self , closure_id : LocalDefId ) -> Vec < Ty < ' tcx > > {
2020-09-09 05:18:28 +00:00
self . typeck_results
. borrow ( )
2020-11-26 04:56:55 +00:00
. closure_min_captures_flattened ( closure_id )
. map ( | captured_place | {
let upvar_ty = captured_place . place . ty ( ) ;
let capture = captured_place . info . capture_kind ;
debug! (
2020-12-02 08:03:56 +00:00
" final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?} " ,
captured_place . place , upvar_ty , capture , captured_place . mutability ,
2020-11-26 04:56:55 +00:00
) ;
2021-10-13 20:20:10 +00:00
apply_capture_kind_on_capture_ty ( self . tcx , upvar_ty , capture , captured_place . region )
2016-08-31 11:08:22 +00:00
} )
2019-05-04 00:47:16 +00:00
. collect ( )
2015-07-17 12:22:03 +00:00
}
2020-09-09 05:18:28 +00:00
2021-07-09 17:45:26 +00:00
/// Adjusts the closure capture information to ensure that the operations aren't unsafe,
2021-07-09 06:37:37 +00:00
/// and that the path can be captured with required capture kind (depending on use in closure,
/// move closure etc.)
///
/// Returns the set of of adjusted information along with the inferred closure kind and span
/// associated with the closure kind inference.
///
/// Note that we *always* infer a minimal kind, even if
/// we don't always *use* that in the final result (i.e., sometimes
/// we've taken the closure kind from the expectations instead, and
/// for generators we don't even implement the closure traits
/// really).
///
2021-07-09 17:45:26 +00:00
/// If we inferred that the closure needs to be FnMut/FnOnce, last element of the returned tuple
2021-07-09 06:37:37 +00:00
/// contains a `Some()` with the `Place` that caused us to do so.
2021-07-09 04:25:41 +00:00
fn process_collected_capture_information (
& self ,
capture_clause : hir ::CaptureBy ,
capture_information : InferredCaptureInformation < ' tcx > ,
) -> ( InferredCaptureInformation < ' tcx > , ty ::ClosureKind , Option < ( Span , Place < ' tcx > ) > ) {
let mut closure_kind = ty ::ClosureKind ::LATTICE_BOTTOM ;
let mut origin : Option < ( Span , Place < ' tcx > ) > = None ;
2021-10-13 21:14:37 +00:00
let processed = capture_information
. into_iter ( )
. map ( | ( place , mut capture_info ) | {
// Apply rules for safety before inferring closure kind
let ( place , capture_kind ) =
restrict_capture_precision ( place , capture_info . capture_kind ) ;
2021-07-09 06:37:37 +00:00
2021-10-13 21:14:37 +00:00
let ( place , capture_kind ) = truncate_capture_for_optimization ( place , capture_kind ) ;
2021-07-09 07:55:03 +00:00
2021-10-13 21:14:37 +00:00
let usage_span = if let Some ( usage_expr ) = capture_info . path_expr_id {
self . tcx . hir ( ) . span ( usage_expr )
} else {
unreachable! ( )
} ;
2021-07-09 04:25:41 +00:00
2021-10-13 21:14:37 +00:00
let updated = match capture_kind {
ty ::UpvarCapture ::ByValue = > match closure_kind {
ty ::ClosureKind ::Fn | ty ::ClosureKind ::FnMut = > {
( ty ::ClosureKind ::FnOnce , Some ( ( usage_span , place . clone ( ) ) ) )
}
// If closure is already FnOnce, don't update
ty ::ClosureKind ::FnOnce = > ( closure_kind , origin . take ( ) ) ,
} ,
2021-07-09 04:25:41 +00:00
2021-10-13 21:14:37 +00:00
ty ::UpvarCapture ::ByRef (
ty ::BorrowKind ::MutBorrow | ty ::BorrowKind ::UniqueImmBorrow ,
) = > {
match closure_kind {
ty ::ClosureKind ::Fn = > {
( ty ::ClosureKind ::FnMut , Some ( ( usage_span , place . clone ( ) ) ) )
}
// Don't update the origin
ty ::ClosureKind ::FnMut | ty ::ClosureKind ::FnOnce = > {
( closure_kind , origin . take ( ) )
}
2021-07-09 04:25:41 +00:00
}
}
2021-10-13 21:14:37 +00:00
_ = > ( closure_kind , origin . take ( ) ) ,
} ;
2021-07-09 04:25:41 +00:00
2021-10-13 21:14:37 +00:00
closure_kind = updated . 0 ;
origin = updated . 1 ;
2021-07-09 04:25:41 +00:00
2021-10-13 21:14:37 +00:00
let ( place , capture_kind ) = match capture_clause {
hir ::CaptureBy ::Value = > adjust_for_move_closure ( place , capture_kind ) ,
hir ::CaptureBy ::Ref = > adjust_for_non_move_closure ( place , capture_kind ) ,
} ;
2021-09-01 07:00:50 +00:00
2021-10-13 21:14:37 +00:00
// This restriction needs to be applied after we have handled adjustments for `move`
// closures. We want to make sure any adjustment that might make us move the place into
// the closure gets handled.
let ( place , capture_kind ) =
restrict_precision_for_drop_types ( self , place , capture_kind , usage_span ) ;
2021-09-01 07:00:50 +00:00
2021-10-13 21:14:37 +00:00
capture_info . capture_kind = capture_kind ;
( place , capture_info )
} )
. collect ( ) ;
2021-07-09 04:25:41 +00:00
( processed , closure_kind , origin )
}
2020-11-09 05:15:45 +00:00
/// Analyzes the information collected by `InferBorrowKind` to compute the min number of
2020-09-26 21:07:00 +00:00
/// Places (and corresponding capture kind) that we need to keep track of to support all
/// the required captured paths.
///
2020-11-21 09:23:42 +00:00
///
/// 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.
///
2020-09-26 21:07:00 +00:00
/// Eg:
2022-04-15 22:04:34 +00:00
/// ```
/// #[derive(Debug)]
2020-09-26 21:07:00 +00:00
/// struct Point { x: i32, y: i32 }
///
2022-04-15 22:04:34 +00:00
/// let s = String::from("s"); // hir_id_s
/// let mut p = Point { x: 2, y: -2 }; // his_id_p
2020-09-26 21:07:00 +00:00
/// let c = || {
2022-04-15 22:04:34 +00:00
/// println!("{s:?}"); // L1
2020-09-26 21:07:00 +00:00
/// p.x += 10; // L2
2022-02-12 19:16:17 +00:00
/// println!("{}" , p.y); // L3
2022-04-15 22:04:34 +00:00
/// println!("{p:?}"); // L4
2020-09-26 21:07:00 +00:00
/// drop(s); // L5
/// };
/// ```
/// and let hir_id_L1..5 be the expressions pointing to use of a captured variable on
/// the lines L1..5 respectively.
///
/// InferBorrowKind results in a structure like this:
///
2022-04-15 22:04:34 +00:00
/// ```ignore (illustrative)
2020-09-26 21:07:00 +00:00
/// {
2020-11-23 01:03:42 +00:00
/// Place(base: hir_id_s, projections: [], ....) -> {
/// capture_kind_expr: hir_id_L5,
/// path_expr_id: hir_id_L5,
/// capture_kind: ByValue
/// },
/// Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> {
/// capture_kind_expr: hir_id_L2,
/// path_expr_id: hir_id_L2,
/// capture_kind: ByValue
/// },
/// Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> {
/// capture_kind_expr: hir_id_L3,
/// path_expr_id: hir_id_L3,
/// capture_kind: ByValue
/// },
/// Place(base: hir_id_p, projections: [], ...) -> {
/// capture_kind_expr: hir_id_L4,
/// path_expr_id: hir_id_L4,
/// capture_kind: ByValue
/// },
2022-05-06 07:17:02 +00:00
/// }
2020-09-26 21:07:00 +00:00
/// ```
///
/// After the min capture analysis, we get:
2022-04-15 22:04:34 +00:00
/// ```ignore (illustrative)
2020-09-26 21:07:00 +00:00
/// {
/// hir_id_s -> [
2020-11-23 01:03:42 +00:00
/// Place(base: hir_id_s, projections: [], ....) -> {
/// capture_kind_expr: hir_id_L5,
/// path_expr_id: hir_id_L5,
/// capture_kind: ByValue
/// },
2020-09-26 21:07:00 +00:00
/// ],
/// hir_id_p -> [
2020-11-23 01:03:42 +00:00
/// Place(base: hir_id_p, projections: [], ...) -> {
/// capture_kind_expr: hir_id_L2,
/// path_expr_id: hir_id_L4,
/// capture_kind: ByValue
/// },
2020-09-26 21:07:00 +00:00
/// ],
2022-05-06 07:17:02 +00:00
/// }
2020-09-26 21:07:00 +00:00
/// ```
fn compute_min_captures (
2020-09-09 05:18:28 +00:00
& self ,
2022-07-12 14:10:22 +00:00
closure_def_id : LocalDefId ,
2020-11-21 09:23:42 +00:00
capture_information : InferredCaptureInformation < ' tcx > ,
2021-10-13 20:20:10 +00:00
closure_span : Span ,
2020-09-09 05:18:28 +00:00
) {
2020-11-21 09:23:42 +00:00
if capture_information . is_empty ( ) {
return ;
}
let mut typeck_results = self . typeck_results . borrow_mut ( ) ;
2020-09-09 05:18:28 +00:00
2020-11-21 09:23:42 +00:00
let mut root_var_min_capture_list =
typeck_results . closure_min_captures . remove ( & closure_def_id ) . unwrap_or_default ( ) ;
2021-08-23 19:59:46 +00:00
for ( mut place , capture_info ) in capture_information . into_iter ( ) {
2020-09-26 21:07:00 +00:00
let var_hir_id = match place . base {
PlaceBase ::Upvar ( upvar_id ) = > upvar_id . var_path . hir_id ,
2020-09-09 05:18:28 +00:00
base = > bug! ( " Expected upvar, found={:?} " , base ) ,
} ;
2022-02-18 23:44:45 +00:00
let Some ( min_cap_list ) = root_var_min_capture_list . get_mut ( & var_hir_id ) else {
let mutability = self . determine_capture_mutability ( & typeck_results , & place ) ;
let min_cap_list = vec! [ ty ::CapturedPlace {
place ,
info : capture_info ,
mutability ,
region : None ,
} ] ;
root_var_min_capture_list . insert ( var_hir_id , min_cap_list ) ;
continue ;
2020-10-17 05:49:11 +00:00
} ;
2020-09-26 21:07:00 +00:00
// Go through each entry in the current list of min_captures
// - if ancestor is found, update it's capture kind to account for current place's
// capture information.
//
// - if descendant is found, remove it from the list, and update the current place's
2022-03-30 19:14:15 +00:00
// capture information to account for the descendant's capture kind.
2020-09-26 21:07:00 +00:00
//
// We can never be in a case where the list contains both an ancestor and a descendant
// Also there can only be ancestor but in case of descendants there might be
// multiple.
let mut descendant_found = false ;
let mut updated_capture_info = capture_info ;
min_cap_list . retain ( | possible_descendant | {
match determine_place_ancestry_relation ( & place , & possible_descendant . place ) {
// current place is ancestor of possible_descendant
PlaceAncestryRelation ::Ancestor = > {
descendant_found = true ;
2021-07-27 06:24:46 +00:00
let mut possible_descendant = possible_descendant . clone ( ) ;
2020-11-23 01:03:42 +00:00
let backup_path_expr_id = updated_capture_info . path_expr_id ;
2021-07-27 06:24:46 +00:00
// Truncate the descendant (already in min_captures) to be same as the ancestor to handle any
// possible change in capture mode.
2021-08-23 19:59:46 +00:00
truncate_place_to_len_and_update_capture_kind (
& mut possible_descendant . place ,
& mut possible_descendant . info . capture_kind ,
2021-07-27 06:24:46 +00:00
place . projections . len ( ) ,
) ;
2020-11-09 05:15:45 +00:00
updated_capture_info =
determine_capture_info ( updated_capture_info , possible_descendant . info ) ;
2020-11-23 01:03:42 +00:00
// we need to keep the ancestor's `path_expr_id`
updated_capture_info . path_expr_id = backup_path_expr_id ;
2020-09-26 21:07:00 +00:00
false
}
_ = > true ,
}
} ) ;
let mut ancestor_found = false ;
if ! descendant_found {
for possible_ancestor in min_cap_list . iter_mut ( ) {
match determine_place_ancestry_relation ( & place , & possible_ancestor . place ) {
2021-10-13 21:14:37 +00:00
PlaceAncestryRelation ::SamePlace = > {
ancestor_found = true ;
possible_ancestor . info = determine_capture_info (
possible_ancestor . info ,
updated_capture_info ,
) ;
// Only one related place will be in the list.
break ;
}
2020-09-26 21:07:00 +00:00
// current place is descendant of possible_ancestor
2021-10-13 21:14:37 +00:00
PlaceAncestryRelation ::Descendant = > {
2020-09-26 21:07:00 +00:00
ancestor_found = true ;
2020-11-23 01:03:42 +00:00
let backup_path_expr_id = possible_ancestor . info . path_expr_id ;
2021-07-27 06:24:46 +00:00
// Truncate the descendant (current place) to be same as the ancestor to handle any
// possible change in capture mode.
2021-08-23 19:59:46 +00:00
truncate_place_to_len_and_update_capture_kind (
& mut place ,
& mut updated_capture_info . capture_kind ,
2021-07-27 06:24:46 +00:00
possible_ancestor . place . projections . len ( ) ,
) ;
possible_ancestor . info = determine_capture_info (
possible_ancestor . info ,
updated_capture_info ,
) ;
2020-09-26 21:07:00 +00:00
2020-11-23 01:03:42 +00:00
// we need to keep the ancestor's `path_expr_id`
possible_ancestor . info . path_expr_id = backup_path_expr_id ;
2021-10-13 21:14:37 +00:00
// Only one related place will be in the list.
2020-09-26 21:07:00 +00:00
break ;
}
_ = > { }
}
}
}
// Only need to insert when we don't have an ancestor in the existing min capture list
if ! ancestor_found {
2020-11-21 09:23:42 +00:00
let mutability = self . determine_capture_mutability ( & typeck_results , & place ) ;
2021-10-13 20:20:10 +00:00
let captured_place = ty ::CapturedPlace {
place ,
info : updated_capture_info ,
mutability ,
region : None ,
} ;
2020-09-26 21:07:00 +00:00
min_cap_list . push ( captured_place ) ;
}
2020-09-09 05:18:28 +00:00
}
2021-10-13 20:20:10 +00:00
// For each capture that is determined to be captured by ref, add region info.
for ( _ , captures ) in & mut root_var_min_capture_list {
for capture in captures {
match capture . info . capture_kind {
ty ::UpvarCapture ::ByRef ( _ ) = > {
let PlaceBase ::Upvar ( upvar_id ) = capture . place . base else { bug! ( " expected upvar " ) } ;
let origin = UpvarRegion ( upvar_id , closure_span ) ;
let upvar_region = self . next_region_var ( origin ) ;
capture . region = Some ( upvar_region ) ;
}
_ = > ( ) ,
}
}
}
2021-09-23 20:36:49 +00:00
debug! (
" For closure={:?}, min_captures before sorting={:?} " ,
closure_def_id , root_var_min_capture_list
) ;
// Now that we have the minimized list of captures, sort the captures by field id.
// This causes the closure to capture the upvars in the same order as the fields are
// declared which is also the drop order. Thus, in situations where we capture all the
2022-03-30 19:14:15 +00:00
// fields of some type, the observable drop order will remain the same as it previously
2021-09-23 20:36:49 +00:00
// was even though we're dropping each capture individually.
// See https://github.com/rust-lang/project-rfc-2229/issues/42 and
// `src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs`.
for ( _ , captures ) in & mut root_var_min_capture_list {
captures . sort_by ( | capture1 , capture2 | {
for ( p1 , p2 ) in capture1 . place . projections . iter ( ) . zip ( & capture2 . place . projections ) {
2021-09-24 02:15:12 +00:00
// We do not need to look at the `Projection.ty` fields here because at each
// step of the iteration, the projections will either be the same and therefore
// the types must be as well or the current projection will be different and
// we will return the result of comparing the field indexes.
2021-09-23 20:36:49 +00:00
match ( p1 . kind , p2 . kind ) {
// Paths are the same, continue to next loop.
( ProjectionKind ::Deref , ProjectionKind ::Deref ) = > { }
( ProjectionKind ::Field ( i1 , _ ) , ProjectionKind ::Field ( i2 , _ ) )
if i1 = = i2 = > { }
// Fields are different, compare them.
( ProjectionKind ::Field ( i1 , _ ) , ProjectionKind ::Field ( i2 , _ ) ) = > {
return i1 . cmp ( & i2 ) ;
}
2021-09-24 02:15:12 +00:00
// We should have either a pair of `Deref`s or a pair of `Field`s.
// Anything else is a bug.
(
l @ ( ProjectionKind ::Deref | ProjectionKind ::Field ( .. ) ) ,
r @ ( ProjectionKind ::Deref | ProjectionKind ::Field ( .. ) ) ,
) = > bug! (
" ProjectionKinds Deref and Field were mismatched: ({:?}, {:?}) " ,
l ,
r
) ,
(
l @ ( ProjectionKind ::Index
| ProjectionKind ::Subslice
| ProjectionKind ::Deref
| ProjectionKind ::Field ( .. ) ) ,
r @ ( ProjectionKind ::Index
| ProjectionKind ::Subslice
| ProjectionKind ::Deref
| ProjectionKind ::Field ( .. ) ) ,
) = > bug! (
" ProjectionKinds Index or Subslice were unexpected: ({:?}, {:?}) " ,
l ,
r
) ,
2021-09-23 20:36:49 +00:00
}
}
unreachable! (
" we captured two identical projections: capture1 = {:?}, capture2 = {:?} " ,
capture1 , capture2
) ;
} ) ;
}
debug! (
" For closure={:?}, min_captures after sorting={:#?} " ,
closure_def_id , root_var_min_capture_list
) ;
2020-11-21 09:23:42 +00:00
typeck_results . closure_min_captures . insert ( closure_def_id , root_var_min_capture_list ) ;
2020-09-09 05:18:28 +00:00
}
2021-01-19 08:15:36 +00:00
/// Perform the migration analysis for RFC 2229, and emit lint
/// `disjoint_capture_drop_reorder` if needed.
fn perform_2229_migration_anaysis (
& self ,
2022-07-12 14:10:22 +00:00
closure_def_id : LocalDefId ,
2021-03-29 23:52:59 +00:00
body_id : hir ::BodyId ,
2021-01-19 08:15:36 +00:00
capture_clause : hir ::CaptureBy ,
span : Span ,
) {
2021-04-08 23:09:08 +00:00
let ( need_migrations , reasons ) = self . compute_2229_migrations (
2021-01-19 08:15:36 +00:00
closure_def_id ,
span ,
capture_clause ,
self . typeck_results . borrow ( ) . closure_min_captures . get ( & closure_def_id ) ,
) ;
if ! need_migrations . is_empty ( ) {
2021-04-01 20:49:05 +00:00
let ( migration_string , migrated_variables_concat ) =
migration_suggestion_for_2229 ( self . tcx , & need_migrations ) ;
2021-01-19 08:15:36 +00:00
2022-07-12 14:10:22 +00:00
let closure_hir_id = self . tcx . hir ( ) . local_def_id_to_hir_id ( closure_def_id ) ;
2022-06-27 05:45:35 +00:00
let closure_head_span = self . tcx . def_span ( closure_def_id ) ;
2021-01-19 08:15:36 +00:00
self . tcx . struct_span_lint_hir (
2021-06-29 13:35:15 +00:00
lint ::builtin ::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES ,
2021-01-19 08:15:36 +00:00
closure_hir_id ,
2022-06-27 05:45:35 +00:00
closure_head_span ,
2022-09-16 07:01:02 +00:00
reasons . migration_message ( ) ,
2021-01-19 08:15:36 +00:00
| lint | {
2021-11-04 21:44:29 +00:00
for NeededMigration { var_hir_id , diagnostics_info } in & need_migrations {
2021-07-08 21:04:58 +00:00
// Labels all the usage of the captured variable and why they are responsible
// for migration being needed
2021-11-04 21:44:29 +00:00
for lint_note in diagnostics_info . iter ( ) {
match & lint_note . captures_info {
2021-11-05 16:43:42 +00:00
UpvarMigrationInfo ::CapturingPrecise { source_expr : Some ( capture_expr_id ) , var_name : captured_name } = > {
2021-11-04 21:44:29 +00:00
let cause_span = self . tcx . hir ( ) . span ( * capture_expr_id ) ;
2022-09-16 07:01:02 +00:00
lint . span_label ( cause_span , format! ( " in Rust 2018, this closure captures all of ` {} `, but in Rust 2021, it will only capture ` {} ` " ,
2021-11-04 21:44:29 +00:00
self . tcx . hir ( ) . name ( * var_hir_id ) ,
captured_name ,
) ) ;
}
2021-11-05 16:43:42 +00:00
UpvarMigrationInfo ::CapturingNothing { use_span } = > {
2022-09-16 07:01:02 +00:00
lint . span_label ( * use_span , format! ( " in Rust 2018, this causes the closure to capture ` {} `, but in Rust 2021, it has no effect " ,
2021-11-05 01:26:47 +00:00
self . tcx . hir ( ) . name ( * var_hir_id ) ,
) ) ;
}
2021-11-04 21:44:29 +00:00
_ = > { }
2021-07-06 20:48:49 +00:00
}
2021-07-07 14:29:06 +00:00
2021-07-08 21:04:58 +00:00
// Add a label pointing to where a captured variable affected by drop order
// is dropped
2021-11-04 21:44:29 +00:00
if lint_note . reason . drop_order {
2022-03-08 14:39:52 +00:00
let drop_location_span = drop_location_span ( self . tcx , closure_hir_id ) ;
2021-07-07 14:29:06 +00:00
2021-11-04 21:44:29 +00:00
match & lint_note . captures_info {
2021-11-05 16:43:42 +00:00
UpvarMigrationInfo ::CapturingPrecise { var_name : captured_name , .. } = > {
2022-09-16 07:01:02 +00:00
lint . span_label ( drop_location_span , format! ( " in Rust 2018, ` {} ` is dropped here, but in Rust 2021, only ` {} ` will be dropped here as part of the closure " ,
2021-11-04 21:44:29 +00:00
self . tcx . hir ( ) . name ( * var_hir_id ) ,
captured_name ,
) ) ;
}
2021-11-05 16:43:42 +00:00
UpvarMigrationInfo ::CapturingNothing { use_span : _ } = > {
2022-09-16 07:01:02 +00:00
lint . span_label ( drop_location_span , format! ( " in Rust 2018, ` {v} ` is dropped here along with the closure, but in Rust 2021 ` {v} ` is not part of the closure " ,
2021-11-05 01:26:47 +00:00
v = self . tcx . hir ( ) . name ( * var_hir_id ) ,
) ) ;
}
2021-11-04 21:44:29 +00:00
}
2021-07-08 21:04:58 +00:00
}
// Add a label explaining why a closure no longer implements a trait
2021-11-04 21:44:29 +00:00
for & missing_trait in & lint_note . reason . auto_traits {
// not capturing something anymore cannot cause a trait to fail to be implemented:
match & lint_note . captures_info {
2021-11-05 16:43:42 +00:00
UpvarMigrationInfo ::CapturingPrecise { var_name : captured_name , .. } = > {
let var_name = self . tcx . hir ( ) . name ( * var_hir_id ) ;
2022-09-16 07:01:02 +00:00
lint . span_label ( closure_head_span , format! ( " \
2021-11-05 16:43:42 +00:00
in Rust 2018 , this closure implements { missing_trait } \
as ` { var_name } ` implements { missing_trait } , but in Rust 2021 , \
this closure will no longer implement { missing_trait } \
because ` { var_name } ` is not fully captured \
and ` { captured_name } ` does not implement { missing_trait } " ));
2021-11-04 21:44:29 +00:00
}
2021-11-05 01:26:47 +00:00
// Cannot happen: if we don't capture a variable, we impl strictly more traits
2021-11-05 16:43:42 +00:00
UpvarMigrationInfo ::CapturingNothing { use_span } = > span_bug! ( * use_span , " missing trait from not capturing something " ) ,
2021-11-04 21:44:29 +00:00
}
2021-07-08 21:04:58 +00:00
}
2021-07-07 15:04:28 +00:00
}
2021-07-06 20:48:49 +00:00
}
2022-09-16 07:01:02 +00:00
lint . note ( " for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html> " ) ;
2021-08-12 09:57:32 +00:00
2021-08-12 10:17:25 +00:00
let diagnostic_msg = format! (
" add a dummy let to cause {} to be fully captured " ,
migrated_variables_concat
) ;
2022-06-27 05:45:35 +00:00
let closure_span = self . tcx . hir ( ) . span_with_body ( closure_hir_id ) ;
2021-08-31 20:17:51 +00:00
let mut closure_body_span = {
// If the body was entirely expanded from a macro
// invocation, i.e. the body is not contained inside the
// closure span, then we walk up the expansion until we
// find the span before the expansion.
2022-06-27 05:45:35 +00:00
let s = self . tcx . hir ( ) . span_with_body ( body_id . hir_id ) ;
2021-08-31 20:17:51 +00:00
s . find_ancestor_inside ( closure_span ) . unwrap_or ( s )
} ;
if let Ok ( mut s ) = self . tcx . sess . source_map ( ) . span_to_snippet ( closure_body_span ) {
if s . starts_with ( '$' ) {
// Looks like a macro fragment. Try to find the real block.
if let Some ( hir ::Node ::Expr ( & hir ::Expr {
kind : hir ::ExprKind ::Block ( block , .. ) , ..
} ) ) = self . tcx . hir ( ) . find ( body_id . hir_id ) {
// If the body is a block (with `{..}`), we use the span of that block.
// E.g. with a `|| $body` expanded from a `m!({ .. })`, we use `{ .. }`, and not `$body`.
// Since we know it's a block, we know we can insert the `let _ = ..` without
// breaking the macro syntax.
if let Ok ( snippet ) = self . tcx . sess . source_map ( ) . span_to_snippet ( block . span ) {
closure_body_span = block . span ;
s = snippet ;
}
}
}
2021-08-12 09:57:32 +00:00
2021-08-12 10:17:25 +00:00
let mut lines = s . lines ( ) ;
let line1 = lines . next ( ) . unwrap_or_default ( ) ;
if line1 . trim_end ( ) = = " { " {
// This is a multi-line closure with just a `{` on the first line,
// so we put the `let` on its own line.
// We take the indentation from the next non-empty line.
2021-09-30 17:38:50 +00:00
let line2 = lines . find ( | line | ! line . is_empty ( ) ) . unwrap_or_default ( ) ;
2021-08-12 10:17:25 +00:00
let indent = line2 . split_once ( | c : char | ! c . is_whitespace ( ) ) . unwrap_or_default ( ) . 0 ;
2022-09-16 07:01:02 +00:00
lint . span_suggestion (
2021-08-12 10:17:25 +00:00
closure_body_span . with_lo ( closure_body_span . lo ( ) + BytePos ::from_usize ( line1 . len ( ) ) ) . shrink_to_lo ( ) ,
& diagnostic_msg ,
2022-04-15 06:56:32 +00:00
format! ( " \n {indent} {migration_string} ; " ) ,
2021-08-12 10:17:25 +00:00
Applicability ::MachineApplicable ,
) ;
} else if line1 . starts_with ( '{' ) {
// This is a closure with its body wrapped in
// braces, but with more than just the opening
// brace on the first line. We put the `let`
// directly after the `{`.
2022-09-16 07:01:02 +00:00
lint . span_suggestion (
2021-08-12 10:17:25 +00:00
closure_body_span . with_lo ( closure_body_span . lo ( ) + BytePos ( 1 ) ) . shrink_to_lo ( ) ,
& diagnostic_msg ,
2022-04-15 06:56:32 +00:00
format! ( " {migration_string} ; " ) ,
2021-08-12 10:17:25 +00:00
Applicability ::MachineApplicable ,
) ;
} else {
// This is a closure without braces around the body.
// We add braces to add the `let` before the body.
2022-09-16 07:01:02 +00:00
lint . multipart_suggestion (
2021-08-12 10:17:25 +00:00
& diagnostic_msg ,
vec! [
2022-04-15 06:56:32 +00:00
( closure_body_span . shrink_to_lo ( ) , format! ( " {{ {migration_string} ; " ) ) ,
2021-08-12 10:17:25 +00:00
( closure_body_span . shrink_to_hi ( ) , " } " . to_string ( ) ) ,
] ,
Applicability ::MachineApplicable
) ;
}
} else {
2022-09-16 07:01:02 +00:00
lint . span_suggestion (
2021-08-12 10:17:25 +00:00
closure_span ,
& diagnostic_msg ,
migration_string ,
Applicability ::HasPlaceholders
) ;
}
2021-04-01 20:49:05 +00:00
2022-09-16 07:01:02 +00:00
lint
2021-01-19 08:15:36 +00:00
} ,
) ;
}
}
2021-04-08 23:09:08 +00:00
/// Combines all the reasons for 2229 migrations
fn compute_2229_migrations_reasons (
& self ,
2021-11-04 21:44:29 +00:00
auto_trait_reasons : FxHashSet < & 'static str > ,
drop_order : bool ,
) -> MigrationWarningReason {
let mut reasons = MigrationWarningReason ::default ( ) ;
2021-04-08 23:09:08 +00:00
2021-12-08 21:56:26 +00:00
reasons . auto_traits . extend ( auto_trait_reasons ) ;
2021-11-04 21:44:29 +00:00
reasons . drop_order = drop_order ;
2021-04-08 23:09:08 +00:00
2022-05-04 02:17:57 +00:00
// `auto_trait_reasons` are in hashset order, so sort them to put the
// diagnostics we emit later in a cross-platform-consistent order.
reasons . auto_traits . sort_unstable ( ) ;
2021-04-08 23:09:08 +00:00
reasons
}
2021-07-08 21:04:58 +00:00
/// 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 auto-traits
/// differ between the root variable and the captured paths.
///
/// Returns a tuple containing a HashMap of CapturesInfo that maps to a HashSet of trait names
/// if migration is needed for traits for the provided var_hir_id, otherwise returns None
fn compute_2229_migrations_for_trait (
2021-04-08 23:09:08 +00:00
& self ,
min_captures : Option < & ty ::RootVariableMinCaptureList < ' tcx > > ,
var_hir_id : hir ::HirId ,
2021-07-03 19:29:09 +00:00
closure_clause : hir ::CaptureBy ,
2021-11-05 16:43:42 +00:00
) -> Option < FxHashMap < UpvarMigrationInfo , FxHashSet < & 'static str > > > {
2021-07-08 21:04:58 +00:00
let auto_traits_def_id = vec! [
self . tcx . lang_items ( ) . clone_trait ( ) ,
self . tcx . lang_items ( ) . sync_trait ( ) ,
2021-10-02 23:51:01 +00:00
self . tcx . get_diagnostic_item ( sym ::Send ) ,
2021-07-08 21:04:58 +00:00
self . tcx . lang_items ( ) . unpin_trait ( ) ,
self . tcx . get_diagnostic_item ( sym ::unwind_safe_trait ) ,
self . tcx . get_diagnostic_item ( sym ::ref_unwind_safe_trait ) ,
] ;
2021-10-09 17:32:42 +00:00
const AUTO_TRAITS : [ & str ; 6 ] =
[ " `Clone` " , " `Sync` " , " `Send` " , " `Unpin` " , " `UnwindSafe` " , " `RefUnwindSafe` " ] ;
2021-07-08 21:04:58 +00:00
2021-09-30 17:38:50 +00:00
let root_var_min_capture_list = min_captures . and_then ( | m | m . get ( & var_hir_id ) ) ? ;
2021-04-08 23:09:08 +00:00
2022-07-22 18:49:35 +00:00
let ty = self . resolve_vars_if_possible ( self . node_ty ( var_hir_id ) ) ;
2021-04-08 23:09:08 +00:00
2021-07-03 19:29:09 +00:00
let ty = match closure_clause {
hir ::CaptureBy ::Value = > ty , // For move closure the capture kind should be by value
hir ::CaptureBy ::Ref = > {
// For non move closure the capture kind is the max capture kind of all captures
// according to the ordering ImmBorrow < UniqueImmBorrow < MutBorrow < ByValue
let mut max_capture_info = root_var_min_capture_list . first ( ) . unwrap ( ) . info ;
for capture in root_var_min_capture_list . iter ( ) {
max_capture_info = determine_capture_info ( max_capture_info , capture . info ) ;
}
2021-10-13 20:20:10 +00:00
apply_capture_kind_on_capture_ty (
self . tcx ,
ty ,
max_capture_info . capture_kind ,
2022-01-28 00:25:15 +00:00
Some ( self . tcx . lifetimes . re_erased ) ,
2021-10-13 20:20:10 +00:00
)
2021-07-03 19:29:09 +00:00
}
} ;
2021-07-08 21:04:58 +00:00
let mut obligations_should_hold = Vec ::new ( ) ;
// Checks if a root variable implements any of the auto traits
for check_trait in auto_traits_def_id . iter ( ) {
obligations_should_hold . push (
check_trait
. map ( | check_trait | {
self . infcx
. type_implements_trait (
check_trait ,
ty ,
self . tcx . mk_substs_trait ( ty , & [ ] ) ,
self . param_env ,
)
. must_apply_modulo_regions ( )
} )
. unwrap_or ( false ) ,
) ;
}
2021-04-08 23:09:08 +00:00
2021-07-08 21:04:58 +00:00
let mut problematic_captures = FxHashMap ::default ( ) ;
2021-07-03 19:29:09 +00:00
// Check whether captured fields also implement the trait
2021-04-08 23:09:08 +00:00
for capture in root_var_min_capture_list . iter ( ) {
2021-07-03 19:29:09 +00:00
let ty = apply_capture_kind_on_capture_ty (
self . tcx ,
capture . place . ty ( ) ,
capture . info . capture_kind ,
2022-01-28 00:25:15 +00:00
Some ( self . tcx . lifetimes . re_erased ) ,
2021-07-03 19:29:09 +00:00
) ;
2021-04-08 23:09:08 +00:00
2021-07-08 21:04:58 +00:00
// Checks if a capture implements any of the auto traits
let mut obligations_holds_for_capture = Vec ::new ( ) ;
for check_trait in auto_traits_def_id . iter ( ) {
obligations_holds_for_capture . push (
check_trait
. map ( | check_trait | {
self . infcx
. type_implements_trait (
check_trait ,
ty ,
self . tcx . mk_substs_trait ( ty , & [ ] ) ,
self . param_env ,
)
. must_apply_modulo_regions ( )
} )
. unwrap_or ( false ) ,
) ;
2021-04-08 23:09:08 +00:00
}
2021-07-08 21:04:58 +00:00
let mut capture_problems = FxHashSet ::default ( ) ;
2021-04-08 23:09:08 +00:00
2021-07-08 21:04:58 +00:00
// Checks if for any of the auto traits, one or more trait is implemented
// by the root variable but not by the capture
for ( idx , _ ) in obligations_should_hold . iter ( ) . enumerate ( ) {
if ! obligations_holds_for_capture [ idx ] & & obligations_should_hold [ idx ] {
2021-10-09 17:32:42 +00:00
capture_problems . insert ( AUTO_TRAITS [ idx ] ) ;
2021-07-08 21:04:58 +00:00
}
}
2021-05-09 14:06:24 +00:00
2021-09-30 17:38:50 +00:00
if ! capture_problems . is_empty ( ) {
2021-07-08 21:04:58 +00:00
problematic_captures . insert (
2021-11-05 16:43:42 +00:00
UpvarMigrationInfo ::CapturingPrecise {
2021-11-04 16:50:24 +00:00
source_expr : capture . info . path_expr_id ,
var_name : capture . to_string ( self . tcx ) ,
} ,
2021-07-08 21:04:58 +00:00
capture_problems ,
) ;
}
2021-04-08 23:09:08 +00:00
}
2021-09-30 17:38:50 +00:00
if ! problematic_captures . is_empty ( ) {
2021-07-08 21:04:58 +00:00
return Some ( problematic_captures ) ;
2021-04-08 23:09:08 +00:00
}
2021-07-08 21:04:58 +00:00
None
2021-04-08 23:09:08 +00:00
}
2020-12-15 08:38:15 +00:00
/// 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**
2021-02-12 09:07:41 +00:00
/// - One of the paths starting at this root variable, that is not captured needs Drop.
2021-04-13 07:43:11 +00:00
///
2021-07-08 21:04:58 +00:00
/// This function only returns a HashSet of CapturesInfo for significant drops. If there
/// are no significant drops than None is returned
2021-11-04 16:50:24 +00:00
#[ instrument(level = " debug " , skip(self)) ]
2021-04-08 23:09:08 +00:00
fn compute_2229_migrations_for_drop (
2020-12-15 08:38:15 +00:00
& self ,
2022-07-12 14:10:22 +00:00
closure_def_id : LocalDefId ,
2020-12-15 08:38:15 +00:00
closure_span : Span ,
min_captures : Option < & ty ::RootVariableMinCaptureList < ' tcx > > ,
2021-04-08 23:09:08 +00:00
closure_clause : hir ::CaptureBy ,
var_hir_id : hir ::HirId ,
2021-11-05 16:43:42 +00:00
) -> Option < FxHashSet < UpvarMigrationInfo > > {
2022-07-22 18:49:35 +00:00
let ty = self . resolve_vars_if_possible ( self . node_ty ( var_hir_id ) ) ;
2020-12-15 08:38:15 +00:00
2022-07-12 14:10:22 +00:00
if ! ty . has_significant_drop ( self . tcx , self . tcx . param_env ( closure_def_id ) ) {
2021-11-04 16:50:24 +00:00
debug! ( " does not have significant drop " ) ;
2021-07-06 20:48:49 +00:00
return None ;
2021-04-08 23:09:08 +00:00
}
2020-12-15 08:38:15 +00:00
2021-10-16 01:45:14 +00:00
let Some ( root_var_min_capture_list ) = min_captures . and_then ( | m | m . get ( & var_hir_id ) ) else {
2021-04-08 23:09:08 +00:00
// The upvar is mentioned within the closure but no path starting from it is
2021-11-04 16:50:24 +00:00
// used. This occurs when you have (e.g.)
//
// ```
// let x = move || {
// let _ = y;
// });
// ```
debug! ( " no path starting from it is used " ) ;
2020-12-15 08:38:15 +00:00
2021-04-08 23:09:08 +00:00
match closure_clause {
// Only migrate if closure is a move closure
2021-11-04 21:44:29 +00:00
hir ::CaptureBy ::Value = > {
2021-11-05 01:26:47 +00:00
let mut diagnostics_info = FxHashSet ::default ( ) ;
let upvars = self . tcx . upvars_mentioned ( closure_def_id ) . expect ( " must be an upvar " ) ;
let upvar = upvars [ & var_hir_id ] ;
2021-11-05 16:43:42 +00:00
diagnostics_info . insert ( UpvarMigrationInfo ::CapturingNothing { use_span : upvar . span } ) ;
2021-11-04 21:44:29 +00:00
return Some ( diagnostics_info ) ;
}
2021-04-08 23:09:08 +00:00
hir ::CaptureBy ::Ref = > { }
2020-12-15 08:38:15 +00:00
}
2021-07-06 20:48:49 +00:00
return None ;
2021-04-08 23:09:08 +00:00
} ;
2021-11-04 16:50:24 +00:00
debug! ( ? root_var_min_capture_list ) ;
2020-12-15 08:38:15 +00:00
2021-07-06 20:48:49 +00:00
let mut projections_list = Vec ::new ( ) ;
let mut diagnostics_info = FxHashSet ::default ( ) ;
for captured_place in root_var_min_capture_list . iter ( ) {
match captured_place . info . capture_kind {
2021-04-08 23:09:08 +00:00
// Only care about captures that are moved into the closure
2021-10-09 21:11:13 +00:00
ty ::UpvarCapture ::ByValue = > {
2021-07-06 20:48:49 +00:00
projections_list . push ( captured_place . place . projections . as_slice ( ) ) ;
2021-11-05 16:43:42 +00:00
diagnostics_info . insert ( UpvarMigrationInfo ::CapturingPrecise {
2021-11-04 16:50:24 +00:00
source_expr : captured_place . info . path_expr_id ,
var_name : captured_place . to_string ( self . tcx ) ,
} ) ;
2021-07-06 20:48:49 +00:00
}
ty ::UpvarCapture ::ByRef ( .. ) = > { }
}
}
2020-12-15 08:38:15 +00:00
2021-11-04 16:50:24 +00:00
debug! ( ? projections_list ) ;
debug! ( ? diagnostics_info ) ;
2021-04-08 23:09:08 +00:00
let is_moved = ! projections_list . is_empty ( ) ;
2021-11-04 16:50:24 +00:00
debug! ( ? is_moved ) ;
2020-12-15 08:38:15 +00:00
2021-04-08 23:09:08 +00:00
let is_not_completely_captured =
2021-09-30 17:38:50 +00:00
root_var_min_capture_list . iter ( ) . any ( | capture | ! capture . place . projections . is_empty ( ) ) ;
2021-11-04 16:50:24 +00:00
debug! ( ? is_not_completely_captured ) ;
2020-12-15 08:38:15 +00:00
2021-04-08 23:09:08 +00:00
if is_moved
& & is_not_completely_captured
& & self . has_significant_drop_outside_of_captures (
closure_def_id ,
closure_span ,
ty ,
projections_list ,
)
{
2021-07-06 20:48:49 +00:00
return Some ( diagnostics_info ) ;
2021-04-08 23:09:08 +00:00
}
2020-12-27 06:13:51 +00:00
2021-09-30 17:38:50 +00:00
None
2021-04-08 23:09:08 +00:00
}
2021-01-19 08:15:36 +00:00
2021-04-08 23:09:08 +00:00
/// 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 either drop
/// order of some path starting at that root variable **might** be affected or auto-traits
/// differ between the root variable and the captured paths.
///
/// 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**
/// - One of the paths starting at this root variable, that is not captured needs Drop **or**
/// - One of the paths captured does not implement all the auto-traits its root variable
/// implements.
2021-05-09 14:06:24 +00:00
///
2021-07-08 21:04:58 +00:00
/// Returns a tuple containing a vector of MigrationDiagnosticInfo, as well as a String
2021-07-07 23:08:04 +00:00
/// containing the reason why root variables whose HirId is contained in the vector should
2021-07-08 21:04:58 +00:00
/// be captured
2021-11-04 16:50:24 +00:00
#[ instrument(level = " debug " , skip(self)) ]
2021-04-08 23:09:08 +00:00
fn compute_2229_migrations (
& self ,
2022-07-12 14:10:22 +00:00
closure_def_id : LocalDefId ,
2021-04-08 23:09:08 +00:00
closure_span : Span ,
closure_clause : hir ::CaptureBy ,
min_captures : Option < & ty ::RootVariableMinCaptureList < ' tcx > > ,
2021-11-04 21:44:29 +00:00
) -> ( Vec < NeededMigration > , MigrationWarningReason ) {
2021-10-16 01:45:14 +00:00
let Some ( upvars ) = self . tcx . upvars_mentioned ( closure_def_id ) else {
2021-11-04 21:44:29 +00:00
return ( Vec ::new ( ) , MigrationWarningReason ::default ( ) ) ;
2021-04-08 23:09:08 +00:00
} ;
2021-01-19 08:15:36 +00:00
2021-04-08 23:09:08 +00:00
let mut need_migrations = Vec ::new ( ) ;
2021-07-08 21:04:58 +00:00
let mut auto_trait_migration_reasons = FxHashSet ::default ( ) ;
let mut drop_migration_needed = false ;
2021-04-08 23:09:08 +00:00
// Perform auto-trait analysis
for ( & var_hir_id , _ ) in upvars . iter ( ) {
2021-11-04 21:44:29 +00:00
let mut diagnostics_info = Vec ::new ( ) ;
2021-07-06 20:48:49 +00:00
2021-07-08 21:04:58 +00:00
let auto_trait_diagnostic = if let Some ( diagnostics_info ) =
2021-07-03 19:29:09 +00:00
self . compute_2229_migrations_for_trait ( min_captures , var_hir_id , closure_clause )
2021-02-12 09:07:41 +00:00
{
2021-07-08 21:04:58 +00:00
diagnostics_info
} else {
FxHashMap ::default ( )
} ;
let drop_reorder_diagnostic = if let Some ( diagnostics_info ) = self
. compute_2229_migrations_for_drop (
closure_def_id ,
closure_span ,
min_captures ,
closure_clause ,
var_hir_id ,
) {
drop_migration_needed = true ;
diagnostics_info
} else {
FxHashSet ::default ( )
} ;
// Combine all the captures responsible for needing migrations into one HashSet
2021-07-09 14:18:55 +00:00
let mut capture_diagnostic = drop_reorder_diagnostic . clone ( ) ;
2021-07-08 21:04:58 +00:00
for key in auto_trait_diagnostic . keys ( ) {
2021-07-09 14:18:55 +00:00
capture_diagnostic . insert ( key . clone ( ) ) ;
2021-04-08 23:09:08 +00:00
}
2021-07-09 17:32:30 +00:00
let mut capture_diagnostic = capture_diagnostic . into_iter ( ) . collect ::< Vec < _ > > ( ) ;
capture_diagnostic . sort ( ) ;
2021-11-04 21:44:29 +00:00
for captures_info in capture_diagnostic {
2021-07-08 21:04:58 +00:00
// Get the auto trait reasons of why migration is needed because of that capture, if there are any
let capture_trait_reasons =
2021-11-04 21:44:29 +00:00
if let Some ( reasons ) = auto_trait_diagnostic . get ( & captures_info ) {
2021-07-08 21:04:58 +00:00
reasons . clone ( )
} else {
FxHashSet ::default ( )
} ;
// Check if migration is needed because of drop reorder as a result of that capture
2021-11-04 21:44:29 +00:00
let capture_drop_reorder_reason = drop_reorder_diagnostic . contains ( & captures_info ) ;
2021-07-08 21:04:58 +00:00
// Combine all the reasons of why the root variable should be captured as a result of
// auto trait implementation issues
2022-08-13 13:50:01 +00:00
auto_trait_migration_reasons . extend ( capture_trait_reasons . iter ( ) . copied ( ) ) ;
2021-07-08 21:04:58 +00:00
2021-11-04 21:44:29 +00:00
diagnostics_info . push ( MigrationLintNote {
captures_info ,
reason : self . compute_2229_migrations_reasons (
capture_trait_reasons ,
capture_drop_reorder_reason ,
) ,
} ) ;
2021-04-08 23:09:08 +00:00
}
2021-11-04 21:44:29 +00:00
if ! diagnostics_info . is_empty ( ) {
need_migrations . push ( NeededMigration { var_hir_id , diagnostics_info } ) ;
2020-12-15 08:38:15 +00:00
}
}
2021-04-08 23:09:08 +00:00
(
need_migrations ,
2021-07-08 21:04:58 +00:00
self . compute_2229_migrations_reasons (
auto_trait_migration_reasons ,
drop_migration_needed ,
) ,
2021-04-08 23:09:08 +00:00
)
2020-12-15 08:38:15 +00:00
}
2020-12-27 06:13:51 +00:00
/// This is a helper function to `compute_2229_migrations_precise_pass`. Provided the type
/// of a root variable and a list of captured paths starting at this root variable (expressed
/// using list of `Projection` slices), it returns true if there is a path that is not
/// captured starting at this root variable that implements Drop.
///
/// The way this function works is at a given call it looks at type `base_path_ty` of some base
2021-02-05 00:55:16 +00:00
/// path say P and then list of projection slices which represent the different captures moved
/// into the closure starting off of P.
2020-12-27 06:13:51 +00:00
///
/// This will make more sense with an example:
///
/// ```rust
/// #![feature(capture_disjoint_fields)]
///
/// struct FancyInteger(i32); // This implements Drop
///
/// struct Point { x: FancyInteger, y: FancyInteger }
/// struct Color;
///
/// struct Wrapper { p: Point, c: Color }
///
/// fn f(w: Wrapper) {
/// let c = || {
/// // Closure captures w.p.x and w.c by move.
/// };
///
/// c();
/// }
/// ```
///
/// If `capture_disjoint_fields` wasn't enabled the closure would've moved `w` instead of the
/// precise paths. If we look closely `w.p.y` isn't captured which implements Drop and
/// therefore Drop ordering would change and we want this function to return true.
///
/// Call stack to figure out if we need to migrate for `w` would look as follows:
///
/// Our initial base path is just `w`, and the paths captured from it are `w[p, x]` and
/// `w[c]`.
/// Notation:
/// - Ty(place): Type of place
2021-03-31 08:40:31 +00:00
/// - `(a, b)`: Represents the function parameters `base_path_ty` and `captured_by_move_projs`
2020-12-27 06:13:51 +00:00
/// respectively.
2022-04-15 22:04:34 +00:00
/// ```ignore (illustrative)
2020-12-27 06:13:51 +00:00
/// (Ty(w), [ &[p, x], &[c] ])
2022-04-15 22:04:34 +00:00
/// // |
/// // ----------------------------
/// // | |
/// // v v
2020-12-27 06:13:51 +00:00
/// (Ty(w.p), [ &[x] ]) (Ty(w.c), [ &[] ]) // I(1)
2022-04-15 22:04:34 +00:00
/// // | |
/// // v v
2020-12-27 06:13:51 +00:00
/// (Ty(w.p), [ &[x] ]) false
2022-04-15 22:04:34 +00:00
/// // |
/// // |
/// // -------------------------------
/// // | |
/// // v v
2020-12-27 06:13:51 +00:00
/// (Ty((w.p).x), [ &[] ]) (Ty((w.p).y), []) // IMP 2
2022-04-15 22:04:34 +00:00
/// // | |
/// // v v
2021-04-13 07:43:11 +00:00
/// false NeedsSignificantDrop(Ty(w.p.y))
2022-04-15 22:04:34 +00:00
/// // |
/// // v
2020-12-27 06:13:51 +00:00
/// true
/// ```
///
/// IMP 1 `(Ty(w.c), [ &[] ])`: Notice the single empty slice inside `captured_projs`.
/// This implies that the `w.c` is completely captured by the closure.
/// Since drop for this path will be called when the closure is
/// dropped we don't need to migrate for it.
///
/// IMP 2 `(Ty((w.p).y), [])`: Notice that `captured_projs` is empty. This implies that this
/// path wasn't captured by the closure. Also note that even
/// though we didn't capture this path, the function visits it,
/// which is kind of the point of this function. We then return
/// if the type of `w.p.y` implements Drop, which in this case is
/// true.
///
/// Consider another example:
///
2022-04-15 22:04:34 +00:00
/// ```ignore (pseudo-rust)
2020-12-27 06:13:51 +00:00
/// struct X;
/// impl Drop for X {}
///
/// struct Y(X);
/// impl Drop for Y {}
///
/// fn foo() {
/// let y = Y(X);
/// let c = || move(y.0);
/// }
/// ```
///
/// Note that `y.0` is captured by the closure. When this function is called for `y`, it will
/// return true, because even though all paths starting at `y` are captured, `y` itself
/// implements Drop which will be affected since `y` isn't completely captured.
fn has_significant_drop_outside_of_captures (
& self ,
2022-07-12 14:10:22 +00:00
closure_def_id : LocalDefId ,
2020-12-27 06:13:51 +00:00
closure_span : Span ,
base_path_ty : Ty < ' tcx > ,
2021-03-31 08:40:31 +00:00
captured_by_move_projs : Vec < & [ Projection < ' tcx > ] > ,
2020-12-27 06:13:51 +00:00
) -> bool {
2022-07-12 14:10:22 +00:00
let needs_drop =
| ty : Ty < ' tcx > | ty . has_significant_drop ( self . tcx , self . tcx . param_env ( closure_def_id ) ) ;
2020-12-27 06:13:51 +00:00
let is_drop_defined_for_ty = | ty : Ty < ' tcx > | {
let drop_trait = self . tcx . require_lang_item ( hir ::LangItem ::Drop , Some ( closure_span ) ) ;
let ty_params = self . tcx . mk_substs_trait ( base_path_ty , & [ ] ) ;
2021-07-06 09:38:15 +00:00
self . infcx
. type_implements_trait (
2021-07-04 15:26:32 +00:00
drop_trait ,
ty ,
ty_params ,
2022-07-12 14:10:22 +00:00
self . tcx . param_env ( closure_def_id ) ,
2021-07-06 09:38:15 +00:00
)
2021-07-04 15:26:32 +00:00
. must_apply_modulo_regions ( )
2020-12-27 06:13:51 +00:00
} ;
let is_drop_defined_for_ty = is_drop_defined_for_ty ( base_path_ty ) ;
// If there is a case where no projection is applied on top of current place
// then there must be exactly one capture corresponding to such a case. Note that this
// represents the case of the path being completely captured by the variable.
//
// eg. If `a.b` is captured and we are processing `a.b`, then we can't have the closure also
2022-03-16 12:12:30 +00:00
// capture `a.b.c`, because that violates min capture.
2021-03-31 08:40:31 +00:00
let is_completely_captured = captured_by_move_projs . iter ( ) . any ( | projs | projs . is_empty ( ) ) ;
2020-12-27 06:13:51 +00:00
2021-03-31 08:40:31 +00:00
assert! ( ! is_completely_captured | | ( captured_by_move_projs . len ( ) = = 1 ) ) ;
2020-12-27 06:13:51 +00:00
if is_completely_captured {
// The place is captured entirely, so doesn't matter if needs dtor, it will be drop
// when the closure is dropped.
return false ;
}
2021-03-31 08:40:31 +00:00
if captured_by_move_projs . is_empty ( ) {
return needs_drop ( base_path_ty ) ;
}
2021-02-12 09:07:41 +00:00
if is_drop_defined_for_ty {
// If drop is implemented for this type then we need it to be fully captured,
2021-03-31 08:40:31 +00:00
// and we know it is not completely captured because of the previous checks.
2021-02-12 09:07:41 +00:00
2021-03-31 08:40:31 +00:00
// Note that this is a bug in the user code that will be reported by the
// borrow checker, since we can't move out of drop types.
// The bug exists in the user's code pre-migration, and we don't migrate here.
return false ;
2021-02-12 09:07:41 +00:00
}
2020-12-27 06:13:51 +00:00
2021-02-12 09:07:41 +00:00
match base_path_ty . kind ( ) {
2020-12-27 06:13:51 +00:00
// Observations:
2021-03-31 08:40:31 +00:00
// - `captured_by_move_projs` is not empty. Therefore we can call
// `captured_by_move_projs.first().unwrap()` safely.
2022-03-30 19:14:15 +00:00
// - All entries in `captured_by_move_projs` have at least one projection.
2021-03-31 08:40:31 +00:00
// Therefore we can call `captured_by_move_projs.first().unwrap().first().unwrap()` safely.
2020-12-27 06:13:51 +00:00
2021-02-05 00:55:16 +00:00
// We don't capture derefs in case of move captures, which would have be applied to
// access any further paths.
ty ::Adt ( def , _ ) if def . is_box ( ) = > unreachable! ( ) ,
ty ::Ref ( .. ) = > unreachable! ( ) ,
ty ::RawPtr ( .. ) = > unreachable! ( ) ,
2020-12-27 06:13:51 +00:00
ty ::Adt ( def , substs ) = > {
2022-03-16 12:12:30 +00:00
// Multi-variant enums are captured in entirety,
2021-03-31 08:40:31 +00:00
// which would've been handled in the case of single empty slice in `captured_by_move_projs`.
2022-03-04 20:28:41 +00:00
assert_eq! ( def . variants ( ) . len ( ) , 1 ) ;
2020-12-27 06:13:51 +00:00
// Only Field projections can be applied to a non-box Adt.
assert! (
2021-03-31 08:40:31 +00:00
captured_by_move_projs . iter ( ) . all ( | projs | matches! (
2020-12-27 06:13:51 +00:00
projs . first ( ) . unwrap ( ) . kind ,
ProjectionKind ::Field ( .. )
) )
) ;
2022-03-04 20:28:41 +00:00
def . variants ( ) . get ( VariantIdx ::new ( 0 ) ) . unwrap ( ) . fields . iter ( ) . enumerate ( ) . any (
2020-12-27 06:13:51 +00:00
| ( i , field ) | {
2021-03-31 08:40:31 +00:00
let paths_using_field = captured_by_move_projs
2020-12-27 06:13:51 +00:00
. iter ( )
. filter_map ( | projs | {
if let ProjectionKind ::Field ( field_idx , _ ) =
projs . first ( ) . unwrap ( ) . kind
{
if ( field_idx as usize ) = = i { Some ( & projs [ 1 .. ] ) } else { None }
} else {
unreachable! ( ) ;
}
} )
. collect ( ) ;
let after_field_ty = field . ty ( self . tcx , substs ) ;
self . has_significant_drop_outside_of_captures (
closure_def_id ,
closure_span ,
after_field_ty ,
paths_using_field ,
)
} ,
)
}
2022-02-07 15:06:31 +00:00
ty ::Tuple ( fields ) = > {
2020-12-27 06:13:51 +00:00
// Only Field projections can be applied to a tuple.
assert! (
2021-03-31 08:40:31 +00:00
captured_by_move_projs . iter ( ) . all ( | projs | matches! (
2020-12-27 06:13:51 +00:00
projs . first ( ) . unwrap ( ) . kind ,
ProjectionKind ::Field ( .. )
) )
) ;
2022-02-07 15:06:31 +00:00
fields . iter ( ) . enumerate ( ) . any ( | ( i , element_ty ) | {
2021-03-31 08:40:31 +00:00
let paths_using_field = captured_by_move_projs
2020-12-27 06:13:51 +00:00
. iter ( )
. filter_map ( | projs | {
if let ProjectionKind ::Field ( field_idx , _ ) = projs . first ( ) . unwrap ( ) . kind
{
if ( field_idx as usize ) = = i { Some ( & projs [ 1 .. ] ) } else { None }
} else {
unreachable! ( ) ;
}
} )
. collect ( ) ;
self . has_significant_drop_outside_of_captures (
closure_def_id ,
closure_span ,
element_ty ,
paths_using_field ,
)
} )
}
2021-02-05 00:55:16 +00:00
// Anything else would be completely captured and therefore handled already.
2020-12-27 06:13:51 +00:00
_ = > unreachable! ( ) ,
}
}
2021-02-04 07:12:31 +00:00
fn init_capture_kind_for_place (
2020-09-09 05:18:28 +00:00
& self ,
2021-02-04 07:12:31 +00:00
place : & Place < ' tcx > ,
2020-09-09 05:18:28 +00:00
capture_clause : hir ::CaptureBy ,
2021-10-13 20:20:10 +00:00
) -> ty ::UpvarCapture {
2020-09-09 05:18:28 +00:00
match capture_clause {
2021-02-04 07:12:31 +00:00
// In case of a move closure if the data is accessed through a reference we
// want to capture by ref to allow precise capture using reborrows.
//
// If the data will be moved out of this place, then the place will be truncated
// at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into
// the closure.
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-25 03:13:38 +00:00
hir ::CaptureBy ::Value if ! place . deref_tys ( ) . any ( Ty ::is_ref ) = > {
2021-10-09 21:11:13 +00:00
ty ::UpvarCapture ::ByValue
2021-02-04 07:12:31 +00:00
}
2021-10-13 20:20:10 +00:00
hir ::CaptureBy ::Value | hir ::CaptureBy ::Ref = > ty ::UpvarCapture ::ByRef ( ty ::ImmBorrow ) ,
2020-09-09 05:18:28 +00:00
}
}
fn place_for_root_variable (
& self ,
closure_def_id : LocalDefId ,
var_hir_id : hir ::HirId ,
) -> Place < ' tcx > {
let upvar_id = ty ::UpvarId ::new ( var_hir_id , closure_def_id ) ;
Place {
base_ty : self . node_ty ( var_hir_id ) ,
base : PlaceBase ::Upvar ( upvar_id ) ,
projections : Default ::default ( ) ,
}
}
2020-10-11 04:14:11 +00:00
2022-07-12 14:10:22 +00:00
fn should_log_capture_analysis ( & self , closure_def_id : LocalDefId ) -> bool {
self . tcx . has_attr ( closure_def_id . to_def_id ( ) , sym ::rustc_capture_analysis )
2020-10-11 04:14:11 +00:00
}
2020-10-17 05:49:11 +00:00
2020-11-13 06:51:19 +00:00
fn log_capture_analysis_first_pass (
2020-10-28 03:41:52 +00:00
& self ,
2022-07-12 14:10:22 +00:00
closure_def_id : LocalDefId ,
2021-10-13 21:14:37 +00:00
capture_information : & InferredCaptureInformation < ' tcx > ,
2020-10-28 03:41:52 +00:00
closure_span : Span ,
) {
if self . should_log_capture_analysis ( closure_def_id ) {
2020-11-13 06:51:19 +00:00
let mut diag =
self . tcx . sess . struct_span_err ( closure_span , " First Pass analysis includes: " ) ;
2020-10-28 03:41:52 +00:00
for ( place , capture_info ) in capture_information {
let capture_str = construct_capture_info_string ( self . tcx , place , capture_info ) ;
2022-04-15 06:56:32 +00:00
let output_str = format! ( " Capturing {capture_str} " ) ;
2020-10-28 03:41:52 +00:00
2020-11-23 01:03:42 +00:00
let span =
capture_info . path_expr_id . map_or ( closure_span , | e | self . tcx . hir ( ) . span ( e ) ) ;
2020-11-13 06:51:19 +00:00
diag . span_note ( span , & output_str ) ;
2020-10-28 03:41:52 +00:00
}
2020-11-13 06:51:19 +00:00
diag . emit ( ) ;
2020-10-28 03:41:52 +00:00
}
}
2022-07-12 14:10:22 +00:00
fn log_closure_min_capture_info ( & self , closure_def_id : LocalDefId , closure_span : Span ) {
2020-10-28 03:41:52 +00:00
if self . should_log_capture_analysis ( closure_def_id ) {
if let Some ( min_captures ) =
self . typeck_results . borrow ( ) . closure_min_captures . get ( & closure_def_id )
{
2020-11-13 06:51:19 +00:00
let mut diag =
self . tcx . sess . struct_span_err ( closure_span , " Min Capture analysis includes: " ) ;
2020-10-28 03:41:52 +00:00
for ( _ , min_captures_for_var ) in min_captures {
for capture in min_captures_for_var {
let place = & capture . place ;
let capture_info = & capture . info ;
let capture_str =
construct_capture_info_string ( self . tcx , place , capture_info ) ;
2022-04-15 06:56:32 +00:00
let output_str = format! ( " Min Capture {capture_str} " ) ;
2020-10-28 03:41:52 +00:00
2020-11-23 01:03:42 +00:00
if capture . info . path_expr_id ! = capture . info . capture_kind_expr_id {
let path_span = capture_info
. path_expr_id
. map_or ( closure_span , | e | self . tcx . hir ( ) . span ( e ) ) ;
let capture_kind_span = capture_info
. capture_kind_expr_id
. map_or ( closure_span , | e | self . tcx . hir ( ) . span ( e ) ) ;
let mut multi_span : MultiSpan =
MultiSpan ::from_spans ( vec! [ path_span , capture_kind_span ] ) ;
let capture_kind_label =
construct_capture_kind_reason_string ( self . tcx , place , capture_info ) ;
let path_label = construct_path_string ( self . tcx , place ) ;
multi_span . push_span_label ( path_span , path_label ) ;
multi_span . push_span_label ( capture_kind_span , capture_kind_label ) ;
diag . span_note ( multi_span , & output_str ) ;
} else {
let span = capture_info
. path_expr_id
. map_or ( closure_span , | e | self . tcx . hir ( ) . span ( e ) ) ;
diag . span_note ( span , & output_str ) ;
} ;
2020-10-28 03:41:52 +00:00
}
}
2020-11-13 06:51:19 +00:00
diag . emit ( ) ;
2020-10-28 03:41:52 +00:00
}
}
}
2020-12-02 08:03:56 +00:00
/// A captured place is mutable if
/// 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.
2020-11-21 09:23:42 +00:00
fn determine_capture_mutability (
& self ,
typeck_results : & ' a TypeckResults < ' tcx > ,
place : & Place < ' tcx > ,
) -> hir ::Mutability {
2020-12-02 08:03:56 +00:00
let var_hir_id = match place . base {
PlaceBase ::Upvar ( upvar_id ) = > upvar_id . var_path . hir_id ,
_ = > unreachable! ( ) ,
} ;
2020-11-21 09:23:42 +00:00
let bm = * typeck_results . pat_binding_modes ( ) . get ( var_hir_id ) . expect ( " missing binding mode " ) ;
2020-12-02 08:03:56 +00:00
let mut is_mutbl = match bm {
ty ::BindByValue ( mutability ) = > mutability ,
ty ::BindByReference ( _ ) = > hir ::Mutability ::Not ,
} ;
for pointer_ty in place . deref_tys ( ) {
match pointer_ty . kind ( ) {
// We don't capture derefs of raw ptrs
ty ::RawPtr ( _ ) = > unreachable! ( ) ,
2022-03-30 19:14:15 +00:00
// Dereferencing a mut-ref allows us to mut the Place if we don't deref
2020-12-02 08:03:56 +00:00
// an immut-ref after on top of this.
ty ::Ref ( .. , hir ::Mutability ::Mut ) = > is_mutbl = hir ::Mutability ::Mut ,
2021-08-22 12:46:15 +00:00
// The place isn't mutable once we dereference an immutable reference.
2020-12-02 08:03:56 +00:00
ty ::Ref ( .. , hir ::Mutability ::Not ) = > return hir ::Mutability ::Not ,
// Dereferencing a box doesn't change mutability
ty ::Adt ( def , .. ) if def . is_box ( ) = > { }
unexpected_ty = > bug! ( " deref of unexpected pointer type {:?} " , unexpected_ty ) ,
}
}
is_mutbl
}
2017-06-08 10:38:30 +00:00
}
2015-07-17 12:22:03 +00:00
2021-03-07 02:47:53 +00:00
/// Truncate the capture so that the place being borrowed is in accordance with RFC 1240,
/// which states that it's unsafe to take a reference into a struct marked `repr(packed)`.
fn restrict_repr_packed_field_ref_capture < ' tcx > (
tcx : TyCtxt < ' tcx > ,
param_env : ty ::ParamEnv < ' tcx > ,
2021-10-13 20:37:53 +00:00
mut place : Place < ' tcx > ,
2021-10-13 20:20:10 +00:00
mut curr_borrow_kind : ty ::UpvarCapture ,
) -> ( Place < ' tcx > , ty ::UpvarCapture ) {
2021-03-07 02:47:53 +00:00
let pos = place . projections . iter ( ) . enumerate ( ) . position ( | ( i , p ) | {
let ty = place . ty_before_projection ( i ) ;
// Return true for fields of packed structs, unless those fields have alignment 1.
match p . kind {
ProjectionKind ::Field ( .. ) = > match ty . kind ( ) {
2022-03-04 20:28:41 +00:00
ty ::Adt ( def , _ ) if def . repr ( ) . packed ( ) = > {
2021-12-24 03:24:51 +00:00
// We erase regions here because they cannot be hashed
match tcx . layout_of ( param_env . and ( tcx . erase_regions ( p . ty ) ) ) {
2021-03-07 02:47:53 +00:00
Ok ( layout ) if layout . align . abi . bytes ( ) = = 1 = > {
// if the alignment is 1, the type can't be further
// disaligned.
debug! (
" restrict_repr_packed_field_ref_capture: ({:?}) - align = 1 " ,
place
) ;
false
}
_ = > {
debug! ( " restrict_repr_packed_field_ref_capture: ({:?}) - true " , place ) ;
true
}
}
}
_ = > false ,
} ,
_ = > false ,
}
} ) ;
if let Some ( pos ) = pos {
2021-08-23 19:59:46 +00:00
truncate_place_to_len_and_update_capture_kind ( & mut place , & mut curr_borrow_kind , pos ) ;
2021-03-07 02:47:53 +00:00
}
2021-08-23 19:59:46 +00:00
( place , curr_borrow_kind )
2021-03-07 02:47:53 +00:00
}
2021-07-03 19:29:09 +00:00
/// Returns a Ty that applies the specified capture kind on the provided capture Ty
2021-12-14 01:45:08 +00:00
fn apply_capture_kind_on_capture_ty < ' tcx > (
2021-07-03 19:29:09 +00:00
tcx : TyCtxt < ' tcx > ,
ty : Ty < ' tcx > ,
2021-10-13 20:20:10 +00:00
capture_kind : UpvarCapture ,
region : Option < ty ::Region < ' tcx > > ,
2021-07-03 19:29:09 +00:00
) -> Ty < ' tcx > {
match capture_kind {
2021-10-09 21:11:13 +00:00
ty ::UpvarCapture ::ByValue = > ty ,
2021-10-13 20:20:10 +00:00
ty ::UpvarCapture ::ByRef ( kind ) = > {
tcx . mk_ref ( region . unwrap ( ) , ty ::TypeAndMut { ty : ty , mutbl : kind . to_mutbl_lossy ( ) } )
}
2021-07-03 19:29:09 +00:00
}
}
2021-07-07 14:29:06 +00:00
/// Returns the Span of where the value with the provided HirId would be dropped
2022-03-08 14:39:52 +00:00
fn drop_location_span < ' tcx > ( tcx : TyCtxt < ' tcx > , hir_id : hir ::HirId ) -> Span {
let owner_id = tcx . hir ( ) . get_enclosing_scope ( hir_id ) . unwrap ( ) ;
2021-07-07 14:29:06 +00:00
let owner_node = tcx . hir ( ) . get ( owner_id ) ;
2021-07-07 22:47:32 +00:00
let owner_span = match owner_node {
2021-07-07 14:29:06 +00:00
hir ::Node ::Item ( item ) = > match item . kind {
2021-07-07 22:47:32 +00:00
hir ::ItemKind ::Fn ( _ , _ , owner_id ) = > tcx . hir ( ) . span ( owner_id . hir_id ) ,
2021-07-07 14:29:06 +00:00
_ = > {
2022-06-28 20:46:15 +00:00
bug! ( " Drop location span error: need to handle more ItemKind '{:?}' " , item . kind ) ;
2021-07-07 14:29:06 +00:00
}
} ,
2021-07-07 22:47:32 +00:00
hir ::Node ::Block ( block ) = > tcx . hir ( ) . span ( block . hir_id ) ,
2022-06-28 20:46:15 +00:00
hir ::Node ::TraitItem ( item ) = > tcx . hir ( ) . span ( item . hir_id ( ) ) ,
hir ::Node ::ImplItem ( item ) = > tcx . hir ( ) . span ( item . hir_id ( ) ) ,
2021-07-07 14:29:06 +00:00
_ = > {
2022-06-28 20:46:15 +00:00
bug! ( " Drop location span error: need to handle more Node '{:?}' " , owner_node ) ;
2021-07-07 14:29:06 +00:00
}
2021-07-07 22:47:32 +00:00
} ;
tcx . sess . source_map ( ) . end_point ( owner_span )
2021-07-07 14:29:06 +00:00
}
2019-06-13 21:48:52 +00:00
struct InferBorrowKind < ' a , ' tcx > {
fcx : & ' a FnCtxt < ' a , ' tcx > ,
2017-11-08 17:36:28 +00:00
// The def-id of the closure whose kind and upvar accesses are being inferred.
2021-10-13 20:37:53 +00:00
closure_def_id : LocalDefId ,
2017-11-08 17:36:28 +00:00
2020-11-09 05:15:45 +00:00
/// For each Place that is captured by the closure, we track the minimal kind of
2020-09-26 21:07:00 +00:00
/// access we need (ref, ref mut, move, etc) and the expression that resulted in such access.
2020-11-09 05:15:45 +00:00
///
2020-09-26 21:07:00 +00:00
/// Consider closure where s.str1 is captured via an ImmutableBorrow and
/// s.str2 via a MutableBorrow
///
2020-11-15 23:06:30 +00:00
/// ```rust,no_run
2022-04-15 22:04:34 +00:00
/// struct SomeStruct { str1: String, str2: String };
2020-11-15 23:06:30 +00:00
///
2020-09-26 21:07:00 +00:00
/// // Assume that the HirId for the variable definition is `V1`
2022-04-15 22:04:34 +00:00
/// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") };
2020-09-26 21:07:00 +00:00
///
/// let fix_s = |new_s2| {
/// // Assume that the HirId for the expression `s.str1` is `E1`
2022-04-15 22:04:34 +00:00
/// println!("Updating SomeStruct with str1={0}", s.str1);
2020-09-26 21:07:00 +00:00
/// // Assume that the HirId for the expression `*s.str2` is `E2`
/// s.str2 = new_s2;
2020-11-15 23:06:30 +00:00
/// };
2020-09-26 21:07:00 +00:00
/// ```
///
/// For closure `fix_s`, (at a high level) the map contains
///
2022-04-15 22:04:34 +00:00
/// ```ignore (illustrative)
2020-09-26 21:07:00 +00:00
/// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow }
/// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
2020-11-21 09:23:42 +00:00
/// ```
capture_information : InferredCaptureInformation < ' tcx > ,
2021-02-25 23:03:41 +00:00
fake_reads : Vec < ( Place < ' tcx > , FakeReadCause , hir ::HirId ) > ,
2017-06-08 10:38:30 +00:00
}
2019-06-13 21:48:52 +00:00
impl < ' a , ' tcx > euv ::Delegate < ' tcx > for InferBorrowKind < ' a , ' tcx > {
2022-05-05 18:59:45 +00:00
fn fake_read (
& mut self ,
place : & PlaceWithHirId < ' tcx > ,
cause : FakeReadCause ,
diag_expr_id : hir ::HirId ,
) {
let PlaceBase ::Upvar ( _ ) = place . place . base else { return } ;
2021-10-13 20:37:53 +00:00
// We need to restrict Fake Read precision to avoid fake reading unsafe code,
// such as deref of a raw pointer.
let dummy_capture_kind = ty ::UpvarCapture ::ByRef ( ty ::BorrowKind ::ImmBorrow ) ;
2022-05-05 18:59:45 +00:00
let ( place , _ ) = restrict_capture_precision ( place . place . clone ( ) , dummy_capture_kind ) ;
2021-10-13 20:37:53 +00:00
let ( place , _ ) = restrict_repr_packed_field_ref_capture (
self . fcx . tcx ,
self . fcx . param_env ,
place ,
dummy_capture_kind ,
) ;
self . fake_reads . push ( ( place , cause , diag_expr_id ) ) ;
2021-02-03 02:07:52 +00:00
}
2021-08-20 13:36:04 +00:00
#[ instrument(skip(self), level = " debug " ) ]
2021-07-14 06:21:08 +00:00
fn consume ( & mut self , place_with_id : & PlaceWithHirId < ' tcx > , diag_expr_id : hir ::HirId ) {
2021-10-13 20:37:53 +00:00
let PlaceBase ::Upvar ( upvar_id ) = place_with_id . place . base else { return } ;
assert_eq! ( self . closure_def_id , upvar_id . closure_expr_id ) ;
2020-09-09 05:18:28 +00:00
2021-10-13 21:14:37 +00:00
self . capture_information . push ( (
2021-10-13 20:37:53 +00:00
place_with_id . place . clone ( ) ,
ty ::CaptureInfo {
capture_kind_expr_id : Some ( diag_expr_id ) ,
path_expr_id : Some ( diag_expr_id ) ,
capture_kind : ty ::UpvarCapture ::ByValue ,
} ,
2021-10-13 21:14:37 +00:00
) ) ;
2015-01-24 22:25:07 +00:00
}
2015-01-02 09:12:25 +00:00
2021-08-20 13:36:04 +00:00
#[ instrument(skip(self), level = " debug " ) ]
2020-11-01 07:15:22 +00:00
fn borrow (
& mut self ,
place_with_id : & PlaceWithHirId < ' tcx > ,
diag_expr_id : hir ::HirId ,
bk : ty ::BorrowKind ,
) {
2021-10-13 20:37:53 +00:00
let PlaceBase ::Upvar ( upvar_id ) = place_with_id . place . base else { return } ;
assert_eq! ( self . closure_def_id , upvar_id . closure_expr_id ) ;
2021-07-27 06:24:46 +00:00
// The region here will get discarded/ignored
2021-10-13 20:37:53 +00:00
let capture_kind = ty ::UpvarCapture ::ByRef ( bk ) ;
2021-07-27 06:24:46 +00:00
2021-06-22 05:58:17 +00:00
// We only want repr packed restriction to be applied to reading references into a packed
2021-07-09 06:37:37 +00:00
// struct, and not when the data is being moved. Therefore we call this method here instead
2021-06-22 05:58:17 +00:00
// of in `restrict_capture_precision`.
2021-10-13 20:37:53 +00:00
let ( place , mut capture_kind ) = restrict_repr_packed_field_ref_capture (
2021-03-07 02:47:53 +00:00
self . fcx . tcx ,
self . fcx . param_env ,
2021-10-13 20:37:53 +00:00
place_with_id . place . clone ( ) ,
capture_kind ,
2021-03-07 02:47:53 +00:00
) ;
2021-06-18 20:35:10 +00:00
2021-10-13 20:37:53 +00:00
// Raw pointers don't inherit mutability
Overhaul `TyS` and `Ty`.
Specifically, change `Ty` from this:
```
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
```
to this
```
pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
```
There are two benefits to this.
- It's now a first class type, so we can define methods on it. This
means we can move a lot of methods away from `TyS`, leaving `TyS` as a
barely-used type, which is appropriate given that it's not meant to
be used directly.
- The uniqueness requirement is now explicit, via the `Interned` type.
E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather
than via `TyS`, which wasn't obvious at all.
Much of this commit is boring churn. The interesting changes are in
these files:
- compiler/rustc_middle/src/arena.rs
- compiler/rustc_middle/src/mir/visit.rs
- compiler/rustc_middle/src/ty/context.rs
- compiler/rustc_middle/src/ty/mod.rs
Specifically:
- Most mentions of `TyS` are removed. It's very much a dumb struct now;
`Ty` has all the smarts.
- `TyS` now has `crate` visibility instead of `pub`.
- `TyS::make_for_test` is removed in favour of the static `BOOL_TY`,
which just works better with the new structure.
- The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls
of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned`
(pointer-based, for the `Equal` case) and partly on `TyS`
(contents-based, for the other cases).
- There are many tedious sigil adjustments, i.e. adding or removing `*`
or `&`. They seem to be unavoidable.
2022-01-25 03:13:38 +00:00
if place_with_id . place . deref_tys ( ) . any ( Ty ::is_unsafe_ptr ) {
2021-10-13 20:37:53 +00:00
capture_kind = ty ::UpvarCapture ::ByRef ( ty ::BorrowKind ::ImmBorrow ) ;
2020-09-09 05:18:28 +00:00
}
2021-10-13 21:14:37 +00:00
self . capture_information . push ( (
2021-10-13 20:37:53 +00:00
place ,
ty ::CaptureInfo {
capture_kind_expr_id : Some ( diag_expr_id ) ,
path_expr_id : Some ( diag_expr_id ) ,
capture_kind ,
2021-07-27 06:24:46 +00:00
} ,
2021-10-13 21:14:37 +00:00
) ) ;
2015-01-02 09:12:25 +00:00
}
2021-08-20 13:36:04 +00:00
#[ instrument(skip(self), level = " debug " ) ]
2020-11-01 07:15:22 +00:00
fn mutate ( & mut self , assignee_place : & PlaceWithHirId < ' tcx > , diag_expr_id : hir ::HirId ) {
2021-03-07 02:47:53 +00:00
self . borrow ( assignee_place , diag_expr_id , ty ::BorrowKind ::MutBorrow ) ;
2015-01-02 09:12:25 +00:00
}
}
2021-09-01 07:00:50 +00:00
/// Rust doesn't permit moving fields out of a type that implements drop
fn restrict_precision_for_drop_types < ' a , ' tcx > (
fcx : & ' a FnCtxt < ' a , ' tcx > ,
mut place : Place < ' tcx > ,
2021-10-13 20:20:10 +00:00
mut curr_mode : ty ::UpvarCapture ,
2021-09-01 07:00:50 +00:00
span : Span ,
2021-10-13 20:20:10 +00:00
) -> ( Place < ' tcx > , ty ::UpvarCapture ) {
2021-09-01 07:00:50 +00:00
let is_copy_type = fcx . infcx . type_is_copy_modulo_regions ( fcx . param_env , place . ty ( ) , span ) ;
2021-10-09 21:11:13 +00:00
if let ( false , UpvarCapture ::ByValue ) = ( is_copy_type , curr_mode ) {
2021-09-01 07:00:50 +00:00
for i in 0 .. place . projections . len ( ) {
match place . ty_before_projection ( i ) . kind ( ) {
ty ::Adt ( def , _ ) if def . destructor ( fcx . tcx ) . is_some ( ) = > {
truncate_place_to_len_and_update_capture_kind ( & mut place , & mut curr_mode , i ) ;
break ;
}
_ = > { }
}
}
}
( place , curr_mode )
}
2021-07-26 06:01:52 +00:00
/// Truncate `place` so that an `unsafe` block isn't required to capture it.
2020-12-04 04:40:09 +00:00
/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
/// them completely.
2021-07-26 06:01:52 +00:00
/// - No projections are applied on top of Union ADTs, since these require unsafe blocks.
2021-12-14 01:45:08 +00:00
fn restrict_precision_for_unsafe < ' tcx > (
2021-08-23 19:59:46 +00:00
mut place : Place < ' tcx > ,
2021-10-13 20:20:10 +00:00
mut curr_mode : ty ::UpvarCapture ,
) -> ( Place < ' tcx > , ty ::UpvarCapture ) {
2020-12-04 04:40:09 +00:00
if place . base_ty . is_unsafe_ptr ( ) {
2021-08-23 19:59:46 +00:00
truncate_place_to_len_and_update_capture_kind ( & mut place , & mut curr_mode , 0 ) ;
2020-12-04 04:40:09 +00:00
}
2021-07-26 06:01:52 +00:00
if place . base_ty . is_union ( ) {
2021-08-23 19:59:46 +00:00
truncate_place_to_len_and_update_capture_kind ( & mut place , & mut curr_mode , 0 ) ;
2021-07-26 06:01:52 +00:00
}
2020-12-04 04:40:09 +00:00
for ( i , proj ) in place . projections . iter ( ) . enumerate ( ) {
if proj . ty . is_unsafe_ptr ( ) {
2021-07-26 06:01:52 +00:00
// Don't apply any projections on top of an unsafe ptr.
2021-08-23 19:59:46 +00:00
truncate_place_to_len_and_update_capture_kind ( & mut place , & mut curr_mode , i + 1 ) ;
break ;
2020-12-04 04:40:09 +00:00
}
2021-07-26 06:01:52 +00:00
if proj . ty . is_union ( ) {
2022-03-30 19:14:15 +00:00
// Don't capture precise fields of a union.
2021-08-23 19:59:46 +00:00
truncate_place_to_len_and_update_capture_kind ( & mut place , & mut curr_mode , i + 1 ) ;
break ;
2021-07-26 06:01:52 +00:00
}
}
2021-07-27 06:24:46 +00:00
( place , curr_mode )
2021-07-26 06:01:52 +00:00
}
/// Truncate projections so that following rules are obeyed by the captured `place`:
/// - No Index projections are captured, since arrays are captured completely.
/// - No unsafe block is required to capture `place`
2022-03-30 19:14:15 +00:00
/// Returns the truncated place and updated capture mode.
2021-07-27 06:24:46 +00:00
fn restrict_capture_precision < ' tcx > (
place : Place < ' tcx > ,
2021-10-13 20:20:10 +00:00
curr_mode : ty ::UpvarCapture ,
) -> ( Place < ' tcx > , ty ::UpvarCapture ) {
2021-08-23 19:59:46 +00:00
let ( mut place , mut curr_mode ) = restrict_precision_for_unsafe ( place , curr_mode ) ;
2021-07-26 06:01:52 +00:00
if place . projections . is_empty ( ) {
// Nothing to do here
2021-07-27 06:24:46 +00:00
return ( place , curr_mode ) ;
2021-07-26 06:01:52 +00:00
}
for ( i , proj ) in place . projections . iter ( ) . enumerate ( ) {
2020-12-04 04:40:09 +00:00
match proj . kind {
ProjectionKind ::Index = > {
// Arrays are completely captured, so we drop Index projections
2021-08-23 19:59:46 +00:00
truncate_place_to_len_and_update_capture_kind ( & mut place , & mut curr_mode , i ) ;
return ( place , curr_mode ) ;
2020-12-04 04:40:09 +00:00
}
2021-02-04 07:12:31 +00:00
ProjectionKind ::Deref = > { }
2020-12-04 04:40:09 +00:00
ProjectionKind ::Field ( .. ) = > { } // ignore
ProjectionKind ::Subslice = > { } // We never capture this
}
}
2021-09-30 17:38:50 +00:00
( place , curr_mode )
2020-12-04 04:40:09 +00:00
}
2021-08-29 20:28:58 +00:00
/// Truncate deref of any reference.
2021-07-09 06:37:37 +00:00
fn adjust_for_move_closure < ' tcx > (
2021-08-23 19:59:46 +00:00
mut place : Place < ' tcx > ,
2021-10-13 20:20:10 +00:00
mut kind : ty ::UpvarCapture ,
) -> ( Place < ' tcx > , ty ::UpvarCapture ) {
2021-07-09 06:37:37 +00:00
let first_deref = place . projections . iter ( ) . position ( | proj | proj . kind = = ProjectionKind ::Deref ) ;
2021-08-29 20:28:58 +00:00
if let Some ( idx ) = first_deref {
truncate_place_to_len_and_update_capture_kind ( & mut place , & mut kind , idx ) ;
2021-02-04 07:12:31 +00:00
}
2021-08-29 20:28:58 +00:00
2021-10-09 21:11:13 +00:00
( place , ty ::UpvarCapture ::ByValue )
2021-07-09 04:25:41 +00:00
}
2021-02-04 07:12:31 +00:00
2021-07-09 06:37:37 +00:00
/// Adjust closure capture just that if taking ownership of data, only move data
/// from enclosing stack frame.
fn adjust_for_non_move_closure < ' tcx > (
2021-08-23 19:59:46 +00:00
mut place : Place < ' tcx > ,
2021-10-13 20:20:10 +00:00
mut kind : ty ::UpvarCapture ,
) -> ( Place < ' tcx > , ty ::UpvarCapture ) {
2021-07-09 04:25:41 +00:00
let contains_deref =
place . projections . iter ( ) . position ( | proj | proj . kind = = ProjectionKind ::Deref ) ;
match kind {
2021-10-09 21:11:13 +00:00
ty ::UpvarCapture ::ByValue = > {
2021-08-23 19:59:46 +00:00
if let Some ( idx ) = contains_deref {
truncate_place_to_len_and_update_capture_kind ( & mut place , & mut kind , idx ) ;
}
2021-07-09 04:25:41 +00:00
}
2021-08-23 19:59:46 +00:00
ty ::UpvarCapture ::ByRef ( .. ) = > { }
2021-07-09 04:25:41 +00:00
}
2021-08-23 19:59:46 +00:00
( place , kind )
2021-02-04 07:12:31 +00:00
}
2021-12-14 01:45:08 +00:00
fn construct_place_string < ' tcx > ( tcx : TyCtxt < '_ > , place : & Place < ' tcx > ) -> String {
2020-10-28 03:41:52 +00:00
let variable_name = match place . base {
PlaceBase ::Upvar ( upvar_id ) = > var_name ( tcx , upvar_id . var_path . hir_id ) . to_string ( ) ,
_ = > bug! ( " Capture_information should only contain upvars " ) ,
} ;
let mut projections_str = String ::new ( ) ;
for ( i , item ) in place . projections . iter ( ) . enumerate ( ) {
let proj = match item . kind {
ProjectionKind ::Field ( a , b ) = > format! ( " ( {:?} , {:?} ) " , a , b ) ,
ProjectionKind ::Deref = > String ::from ( " Deref " ) ,
ProjectionKind ::Index = > String ::from ( " Index " ) ,
ProjectionKind ::Subslice = > String ::from ( " Subslice " ) ,
} ;
if i ! = 0 {
2021-02-12 09:27:08 +00:00
projections_str . push ( ',' ) ;
2020-10-28 03:41:52 +00:00
}
projections_str . push_str ( proj . as_str ( ) ) ;
}
2022-04-15 06:56:32 +00:00
format! ( " {variable_name} [ {projections_str} ] " )
2020-11-23 01:03:42 +00:00
}
2021-12-14 01:45:08 +00:00
fn construct_capture_kind_reason_string < ' tcx > (
2020-11-23 01:03:42 +00:00
tcx : TyCtxt < '_ > ,
place : & Place < ' tcx > ,
2021-10-13 20:20:10 +00:00
capture_info : & ty ::CaptureInfo ,
2020-11-23 01:03:42 +00:00
) -> String {
2021-09-30 17:38:50 +00:00
let place_str = construct_place_string ( tcx , place ) ;
2020-11-23 01:03:42 +00:00
2020-10-28 03:41:52 +00:00
let capture_kind_str = match capture_info . capture_kind {
2021-10-09 21:11:13 +00:00
ty ::UpvarCapture ::ByValue = > " ByValue " . into ( ) ,
2021-10-13 20:20:10 +00:00
ty ::UpvarCapture ::ByRef ( kind ) = > format! ( " {:?} " , kind ) ,
2020-10-28 03:41:52 +00:00
} ;
2020-11-23 01:03:42 +00:00
2022-04-15 06:56:32 +00:00
format! ( " {place_str} captured as {capture_kind_str} here " )
2020-11-23 01:03:42 +00:00
}
2021-12-14 01:45:08 +00:00
fn construct_path_string < ' tcx > ( tcx : TyCtxt < '_ > , place : & Place < ' tcx > ) -> String {
2021-09-30 17:38:50 +00:00
let place_str = construct_place_string ( tcx , place ) ;
2020-11-23 01:03:42 +00:00
2022-04-15 06:56:32 +00:00
format! ( " {place_str} used here " )
2020-11-23 01:03:42 +00:00
}
2021-12-14 01:45:08 +00:00
fn construct_capture_info_string < ' tcx > (
2020-11-23 01:03:42 +00:00
tcx : TyCtxt < '_ > ,
place : & Place < ' tcx > ,
2021-10-13 20:20:10 +00:00
capture_info : & ty ::CaptureInfo ,
2020-11-23 01:03:42 +00:00
) -> String {
2021-09-30 17:38:50 +00:00
let place_str = construct_place_string ( tcx , place ) ;
2020-11-23 01:03:42 +00:00
let capture_kind_str = match capture_info . capture_kind {
2021-10-09 21:11:13 +00:00
ty ::UpvarCapture ::ByValue = > " ByValue " . into ( ) ,
2021-10-13 20:20:10 +00:00
ty ::UpvarCapture ::ByRef ( kind ) = > format! ( " {:?} " , kind ) ,
2020-11-23 01:03:42 +00:00
} ;
2022-04-15 06:56:32 +00:00
format! ( " {place_str} -> {capture_kind_str} " )
2020-10-28 03:41:52 +00:00
}
2020-04-19 11:00:18 +00:00
fn var_name ( tcx : TyCtxt < '_ > , var_hir_id : hir ::HirId ) -> Symbol {
2019-06-19 13:44:51 +00:00
tcx . hir ( ) . name ( var_hir_id )
2017-08-08 12:34:37 +00:00
}
2020-09-26 21:07:00 +00:00
2021-11-04 16:50:24 +00:00
#[ instrument(level = " debug " , skip(tcx)) ]
2021-06-29 15:41:49 +00:00
fn should_do_rust_2021_incompatible_closure_captures_analysis (
tcx : TyCtxt < '_ > ,
closure_id : hir ::HirId ,
) -> bool {
2022-09-04 14:26:48 +00:00
if tcx . sess . rust_2021 ( ) {
return false ;
}
2021-06-29 13:35:15 +00:00
let ( level , _ ) =
tcx . lint_level_at_node ( lint ::builtin ::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES , closure_id ) ;
2020-12-15 08:38:15 +00:00
! matches! ( level , lint ::Level ::Allow )
}
2021-04-01 20:49:05 +00:00
/// Return a two string tuple (s1, s2)
/// - s1: Line of code that is needed for the migration: eg: `let _ = (&x, ...)`.
/// - s2: Comma separated names of the variables being migrated.
fn migration_suggestion_for_2229 (
tcx : TyCtxt < '_ > ,
2022-06-03 16:42:42 +00:00
need_migrations : & [ NeededMigration ] ,
2021-04-01 20:49:05 +00:00
) -> ( String , String ) {
2021-11-04 21:44:29 +00:00
let need_migrations_variables = need_migrations
. iter ( )
. map ( | NeededMigration { var_hir_id : v , .. } | var_name ( tcx , * v ) )
. collect ::< Vec < _ > > ( ) ;
2021-04-01 20:49:05 +00:00
let migration_ref_concat =
2022-04-15 06:56:32 +00:00
need_migrations_variables . iter ( ) . map ( | v | format! ( " & {v} " ) ) . collect ::< Vec < _ > > ( ) . join ( " , " ) ;
2020-12-15 08:38:15 +00:00
2021-04-01 20:49:05 +00:00
let migration_string = if 1 = = need_migrations . len ( ) {
2022-04-15 06:56:32 +00:00
format! ( " let _ = {migration_ref_concat} " )
2021-03-29 23:52:59 +00:00
} else {
2022-04-15 06:56:32 +00:00
format! ( " let _ = ( {migration_ref_concat} ) " )
2021-04-01 20:49:05 +00:00
} ;
let migrated_variables_concat =
2022-04-15 06:56:32 +00:00
need_migrations_variables . iter ( ) . map ( | v | format! ( " ` {v} ` " ) ) . collect ::< Vec < _ > > ( ) . join ( " , " ) ;
2021-04-01 20:49:05 +00:00
( migration_string , migrated_variables_concat )
2020-12-15 08:38:15 +00:00
}
2020-11-09 05:15:45 +00:00
/// Helper function to determine if we need to escalate CaptureKind from
/// CaptureInfo A to B and returns the escalated CaptureInfo.
/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)
///
/// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based
2020-11-23 01:03:42 +00:00
/// on the `CaptureInfo` containing an associated `capture_kind_expr_id`.
///
/// It is the caller's duty to figure out which path_expr_id to use.
2020-11-09 05:15:45 +00:00
///
/// If both the CaptureKind and Expression are considered to be equivalent,
2022-03-30 19:14:15 +00:00
/// then `CaptureInfo` A is preferred. This can be useful in cases where we want to prioritize
2020-11-09 05:15:45 +00:00
/// expressions reported back to the user as part of diagnostics based on which appears earlier
2021-04-19 12:57:08 +00:00
/// in the closure. This can be achieved simply by calling
2020-11-09 05:15:45 +00:00
/// `determine_capture_info(existing_info, current_info)`. This works out because the
/// expressions that occur earlier in the closure body than the current expression are processed before.
/// Consider the following example
2020-11-15 23:06:30 +00:00
/// ```rust,no_run
/// struct Point { x: i32, y: i32 }
2022-04-15 22:04:34 +00:00
/// let mut p = Point { x: 10, y: 10 };
2020-11-09 05:15:45 +00:00
///
/// let c = || {
/// p.x += 10;
/// // ^ E1 ^
/// // ...
/// // More code
/// // ...
/// p.x += 10; // E2
/// // ^ E2 ^
2020-11-15 23:06:30 +00:00
/// };
2020-11-09 05:15:45 +00:00
/// ```
/// `CaptureKind` associated with both `E1` and `E2` will be ByRef(MutBorrow),
2020-11-10 08:05:50 +00:00
/// and both have an expression associated, however for diagnostics we prefer reporting
2020-11-09 05:15:45 +00:00
/// `E1` since it appears earlier in the closure body. When `E2` is being processed we
/// would've already handled `E1`, and have an existing capture_information for it.
/// Calling `determine_capture_info(existing_info_e1, current_info_e2)` will return
/// `existing_info_e1` in this case, allowing us to point to `E1` in case of diagnostics.
2021-10-13 20:20:10 +00:00
fn determine_capture_info (
capture_info_a : ty ::CaptureInfo ,
capture_info_b : ty ::CaptureInfo ,
) -> ty ::CaptureInfo {
2020-11-09 05:15:45 +00:00
// If the capture kind is equivalent then, we don't need to escalate and can compare the
// expressions.
let eq_capture_kind = match ( capture_info_a . capture_kind , capture_info_b . capture_kind ) {
2021-10-09 21:11:13 +00:00
( ty ::UpvarCapture ::ByValue , ty ::UpvarCapture ::ByValue ) = > true ,
2021-10-13 20:20:10 +00:00
( ty ::UpvarCapture ::ByRef ( ref_a ) , ty ::UpvarCapture ::ByRef ( ref_b ) ) = > ref_a = = ref_b ,
2021-10-09 21:11:13 +00:00
( ty ::UpvarCapture ::ByValue , _ ) | ( ty ::UpvarCapture ::ByRef ( _ ) , _ ) = > false ,
2020-11-09 05:15:45 +00:00
} ;
if eq_capture_kind {
2020-11-23 01:03:42 +00:00
match ( capture_info_a . capture_kind_expr_id , capture_info_b . capture_kind_expr_id ) {
2020-11-09 05:15:45 +00:00
( Some ( _ ) , _ ) | ( None , None ) = > capture_info_a ,
( None , Some ( _ ) ) = > capture_info_b ,
}
} else {
// We select the CaptureKind which ranks higher based the following priority order:
// ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow
match ( capture_info_a . capture_kind , capture_info_b . capture_kind ) {
2021-10-09 21:11:13 +00:00
( ty ::UpvarCapture ::ByValue , _ ) = > capture_info_a ,
( _ , ty ::UpvarCapture ::ByValue ) = > capture_info_b ,
2020-11-09 05:15:45 +00:00
( ty ::UpvarCapture ::ByRef ( ref_a ) , ty ::UpvarCapture ::ByRef ( ref_b ) ) = > {
2021-10-13 20:20:10 +00:00
match ( ref_a , ref_b ) {
2020-11-09 05:15:45 +00:00
// Take LHS:
( ty ::UniqueImmBorrow | ty ::MutBorrow , ty ::ImmBorrow )
| ( ty ::MutBorrow , ty ::UniqueImmBorrow ) = > capture_info_a ,
// Take RHS:
( ty ::ImmBorrow , ty ::UniqueImmBorrow | ty ::MutBorrow )
| ( ty ::UniqueImmBorrow , ty ::MutBorrow ) = > capture_info_b ,
( ty ::ImmBorrow , ty ::ImmBorrow )
| ( ty ::UniqueImmBorrow , ty ::UniqueImmBorrow )
| ( ty ::MutBorrow , ty ::MutBorrow ) = > {
bug! ( " Expected unequal capture kinds " ) ;
}
}
}
}
}
}
2021-07-27 06:24:46 +00:00
/// Truncates `place` to have up to `len` projections.
/// `curr_mode` is the current required capture kind for the place.
/// Returns the truncated `place` and the updated required capture kind.
///
/// Note: Capture kind changes from `MutBorrow` to `UniqueImmBorrow` if the truncated part of the `place`
/// contained `Deref` of `&mut`.
2021-12-14 01:45:08 +00:00
fn truncate_place_to_len_and_update_capture_kind < ' tcx > (
2021-08-23 19:59:46 +00:00
place : & mut Place < ' tcx > ,
2021-10-13 20:20:10 +00:00
curr_mode : & mut ty ::UpvarCapture ,
2021-07-27 06:24:46 +00:00
len : usize ,
2021-08-23 19:59:46 +00:00
) {
2021-07-27 06:24:46 +00:00
let is_mut_ref = | ty : Ty < '_ > | matches! ( ty . kind ( ) , ty ::Ref ( .. , hir ::Mutability ::Mut ) ) ;
// If the truncated part of the place contains `Deref` of a `&mut` then convert MutBorrow ->
// UniqueImmBorrow
// Note that if the place contained Deref of a raw pointer it would've not been MutBorrow, so
// we don't need to worry about that case here.
match curr_mode {
2021-10-13 20:20:10 +00:00
ty ::UpvarCapture ::ByRef ( ty ::BorrowKind ::MutBorrow ) = > {
2021-07-27 06:24:46 +00:00
for i in len .. place . projections . len ( ) {
if place . projections [ i ] . kind = = ProjectionKind ::Deref
& & is_mut_ref ( place . ty_before_projection ( i ) )
{
2021-10-13 20:20:10 +00:00
* curr_mode = ty ::UpvarCapture ::ByRef ( ty ::BorrowKind ::UniqueImmBorrow ) ;
2021-07-27 06:24:46 +00:00
break ;
}
}
}
ty ::UpvarCapture ::ByRef ( .. ) = > { }
2021-10-09 21:11:13 +00:00
ty ::UpvarCapture ::ByValue = > { }
2021-07-27 06:24:46 +00:00
}
place . projections . truncate ( len ) ;
}
2020-09-26 21:07:00 +00:00
/// Determines the Ancestry relationship of Place A relative to Place B
///
/// `PlaceAncestryRelation::Ancestor` implies Place A is ancestor of Place B
/// `PlaceAncestryRelation::Descendant` implies Place A is descendant of Place B
/// `PlaceAncestryRelation::Divergent` implies neither of them is the ancestor of the other.
2021-12-14 01:45:08 +00:00
fn determine_place_ancestry_relation < ' tcx > (
2020-09-26 21:07:00 +00:00
place_a : & Place < ' tcx > ,
place_b : & Place < ' tcx > ,
) -> PlaceAncestryRelation {
// If Place A and Place B, don't start off from the same root variable, they are divergent.
if place_a . base ! = place_b . base {
return PlaceAncestryRelation ::Divergent ;
}
// Assume of length of projections_a = n
let projections_a = & place_a . projections ;
// Assume of length of projections_b = m
let projections_b = & place_b . projections ;
2021-02-12 12:58:13 +00:00
let same_initial_projections =
2021-10-08 02:59:56 +00:00
iter ::zip ( projections_a , projections_b ) . all ( | ( proj_a , proj_b ) | proj_a . kind = = proj_b . kind ) ;
2020-09-26 21:07:00 +00:00
if same_initial_projections {
2021-10-09 05:41:05 +00:00
use std ::cmp ::Ordering ;
2020-09-26 21:07:00 +00:00
// First min(n, m) projections are the same
// Select Ancestor/Descendant
2021-10-09 05:41:05 +00:00
match projections_b . len ( ) . cmp ( & projections_a . len ( ) ) {
Ordering ::Greater = > PlaceAncestryRelation ::Ancestor ,
Ordering ::Equal = > PlaceAncestryRelation ::SamePlace ,
Ordering ::Less = > PlaceAncestryRelation ::Descendant ,
2020-09-26 21:07:00 +00:00
}
} else {
PlaceAncestryRelation ::Divergent
}
}
2021-06-21 06:48:18 +00:00
2022-03-16 12:12:30 +00:00
/// Reduces the precision of the captured place when the precision doesn't yield any benefit from
/// borrow checking perspective, allowing us to save us on the size of the capture.
2021-06-28 01:19:39 +00:00
///
///
/// Fields that are read through a shared reference will always be read via a shared ref or a copy,
/// and therefore capturing precise paths yields no benefit. This optimization truncates the
/// rightmost deref of the capture if the deref is applied to a shared ref.
///
/// Reason we only drop the last deref is because of the following edge case:
///
2022-04-15 22:04:34 +00:00
/// ```
/// # struct A { field_of_a: Box<i32> }
/// # struct B {}
/// # struct C<'a>(&'a i32);
2021-06-28 01:19:39 +00:00
/// struct MyStruct<'a> {
/// a: &'static A,
/// b: B,
/// c: C<'a>,
/// }
///
/// fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static {
2022-04-15 22:04:34 +00:00
/// || drop(&*m.a.field_of_a)
2021-06-28 01:19:39 +00:00
/// // Here we really do want to capture `*m.a` because that outlives `'static`
///
/// // If we capture `m`, then the closure no longer outlives `'static'
/// // it is constrained to `'a`
/// }
/// ```
2021-07-27 06:24:46 +00:00
fn truncate_capture_for_optimization < ' tcx > (
2021-08-23 19:59:46 +00:00
mut place : Place < ' tcx > ,
2021-10-13 20:20:10 +00:00
mut curr_mode : ty ::UpvarCapture ,
) -> ( Place < ' tcx > , ty ::UpvarCapture ) {
2021-06-28 01:19:39 +00:00
let is_shared_ref = | ty : Ty < '_ > | matches! ( ty . kind ( ) , ty ::Ref ( .. , hir ::Mutability ::Not ) ) ;
2021-07-07 00:52:53 +00:00
// Find the right-most deref (if any). All the projections that come after this
// are fields or other "in-place pointer adjustments"; these refer therefore to
// data owned by whatever pointer is being dereferenced here.
2021-06-28 01:19:39 +00:00
let idx = place . projections . iter ( ) . rposition ( | proj | ProjectionKind ::Deref = = proj . kind ) ;
match idx {
2021-07-07 00:52:53 +00:00
// If that pointer is a shared reference, then we don't need those fields.
2021-06-28 01:19:39 +00:00
Some ( idx ) if is_shared_ref ( place . ty_before_projection ( idx ) ) = > {
2021-08-23 19:59:46 +00:00
truncate_place_to_len_and_update_capture_kind ( & mut place , & mut curr_mode , idx + 1 )
2021-06-28 01:19:39 +00:00
}
2021-08-23 19:59:46 +00:00
None | Some ( _ ) = > { }
2021-06-28 01:19:39 +00:00
}
2021-08-23 19:59:46 +00:00
( place , curr_mode )
2021-06-28 01:19:39 +00:00
}
2021-06-21 06:48:18 +00:00
/// Precise capture is enabled if the feature gate `capture_disjoint_fields` is enabled or if
/// user is using Rust Edition 2021 or higher.
///
/// `span` is the span of the closure.
fn enable_precise_capture ( tcx : TyCtxt < '_ > , span : Span ) -> bool {
// We use span here to ensure that if the closure was generated by a macro with a different
// edition.
tcx . features ( ) . capture_disjoint_fields | | span . rust_2021 ( )
}