Skip to content

Commit c2deba7

Browse files
committed
Address review comments
1 parent a80f2df commit c2deba7

File tree

3 files changed

+131
-85
lines changed

3 files changed

+131
-85
lines changed

compiler/rustc_middle/src/ty/context.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -415,9 +415,8 @@ pub struct TypeckResults<'tcx> {
415415
/// entire variable.
416416
pub closure_captures: ty::UpvarListMap,
417417

418-
/// Given the closure DefId this map provides a map of
419-
/// root variables to minimum set of `Place`s (and how) that need to be tracked
420-
/// to support all captures of that closure.
418+
/// Tracks the minimum captures required for a closure;
419+
/// see `MinCaptureInformationMap` for more details.
421420
pub closure_min_captures: ty::MinCaptureInformationMap<'tcx>,
422421

423422
/// Stores the type, expression, span and optional scope span of all types

compiler/rustc_middle/src/ty/mod.rs

+25-10
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,31 @@ pub struct UpvarBorrow<'tcx> {
763763
pub region: ty::Region<'tcx>,
764764
}
765765

766+
/// Given the closure DefId this map provides a map of root variables to minimum
767+
/// set of `CapturedPlace`s that need to be tracked to support all captures of that closure.
768+
pub type MinCaptureInformationMap<'tcx> = FxHashMap<DefId, RootVariableMinCaptureList<'tcx>>;
769+
770+
/// Part of `MinCaptureInformationMap`; Maps a root variable to the list of `CapturedPlace`.
771+
/// Used to track the minimum set of `Place`s that need to be captured to support all
772+
/// Places captured by the closure starting at a given root variable.
773+
///
774+
/// This provides a convenient and quick way of checking if a variable being used within
775+
/// a closure is a capture of a local variable.
776+
pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureList<'tcx>>;
777+
778+
/// Part of `MinCaptureInformationMap`; List of `CapturePlace`s.
779+
pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>;
780+
781+
/// A `Place` and the corresponding `CaptureInfo`.
782+
#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
783+
pub struct CapturedPlace<'tcx> {
784+
pub place: HirPlace<'tcx>,
785+
pub info: CaptureInfo<'tcx>,
786+
}
787+
788+
/// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move)
789+
/// for a particular capture as well as identifying the part of the source code
790+
/// that triggered this capture to occur.
766791
#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, HashStable)]
767792
pub struct CaptureInfo<'tcx> {
768793
/// Expr Id pointing to use that resulted in selecting the current capture kind
@@ -788,19 +813,9 @@ pub struct CaptureInfo<'tcx> {
788813
pub capture_kind: UpvarCapture<'tcx>,
789814
}
790815

791-
#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
792-
pub struct CapturedPlace<'tcx> {
793-
pub place: HirPlace<'tcx>,
794-
pub info: CaptureInfo<'tcx>,
795-
}
796-
797816
pub type UpvarListMap = FxHashMap<DefId, FxIndexMap<hir::HirId, UpvarId>>;
798817
pub type UpvarCaptureMap<'tcx> = FxHashMap<UpvarId, UpvarCapture<'tcx>>;
799818

800-
pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>;
801-
pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureList<'tcx>>;
802-
pub type MinCaptureInformationMap<'tcx> = FxHashMap<DefId, RootVariableMinCaptureList<'tcx>>;
803-
804819
#[derive(Clone, Copy, PartialEq, Eq)]
805820
pub enum IntVarValue {
806821
IntType(ast::IntTy),

compiler/rustc_typeck/src/check/upvar.rs

+104-72
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ use rustc_span::{Span, Symbol};
4646

4747
/// Describe the relationship between the paths of two places
4848
/// eg:
49-
/// - foo is ancestor of foo.bar.baz
50-
/// - foo.bar.baz is an descendant of foo.bar,
51-
/// - foo.bar and foo.baz are divergent
49+
/// - `foo` is ancestor of `foo.bar.baz`
50+
/// - `foo.bar.baz` is an descendant of `foo.bar`
51+
/// - `foo.bar` and `foo.baz` are divergent
5252
enum PlaceAncestryRelation {
5353
Ancestor,
5454
Descendant,
@@ -124,7 +124,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
124124

125125
let local_def_id = closure_def_id.expect_local();
126126

127-
let mut capture_information = FxIndexMap::<Place<'tcx>, ty::CaptureInfo<'tcx>>::default();
127+
let mut capture_information: FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>> =
128+
Default::default();
128129
if !self.tcx.features().capture_disjoint_fields {
129130
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
130131
for (&var_hir_id, _) in upvars.iter() {
@@ -186,7 +187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
186187
self.compute_min_captures(closure_def_id, delegate);
187188
self.log_closure_min_capture_info(closure_def_id, span);
188189

189-
self.set_closure_captures(closure_def_id);
190+
self.min_captures_to_closure_captures_bridge(closure_def_id);
190191

191192
// Now that we've analyzed the closure, we know how each
192193
// variable is borrowed, and we know what traits the closure
@@ -274,8 +275,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
274275
///
275276
/// 2. upvar_capture_map
276277
/// UpvarId(foo,c) -> MutBorrow, UpvarId(bar, c) -> ByValue
277-
278-
fn set_closure_captures(&self, closure_def_id: DefId) {
278+
fn min_captures_to_closure_captures_bridge(&self, closure_def_id: DefId) {
279279
let mut closure_captures: FxIndexMap<hir::HirId, ty::UpvarId> = Default::default();
280280
let mut upvar_capture_map = ty::UpvarCaptureMap::default();
281281

@@ -304,8 +304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
304304
// so we create a fake capture info with no expression.
305305
let fake_capture_info =
306306
ty::CaptureInfo { expr_id: None, capture_kind: capture_kind.clone() };
307-
self.determine_capture_info(fake_capture_info, capture_info.clone())
308-
.capture_kind
307+
determine_capture_info(fake_capture_info, capture_info).capture_kind
309308
} else {
310309
capture_info.capture_kind
311310
};
@@ -329,7 +328,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
329328
}
330329
}
331330

332-
/// Analyses the information collected by InferBorrowKind to compute the min number of
331+
/// Analyzes the information collected by `InferBorrowKind` to compute the min number of
333332
/// Places (and corresponding capture kind) that we need to keep track of to support all
334333
/// the required captured paths.
335334
///
@@ -420,8 +419,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
420419
// current place is ancestor of possible_descendant
421420
PlaceAncestryRelation::Ancestor => {
422421
descendant_found = true;
423-
updated_capture_info = self
424-
.determine_capture_info(updated_capture_info, possible_descendant.info);
422+
updated_capture_info =
423+
determine_capture_info(updated_capture_info, possible_descendant.info);
425424
false
426425
}
427426

@@ -437,7 +436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
437436
PlaceAncestryRelation::Descendant => {
438437
ancestor_found = true;
439438
possible_ancestor.info =
440-
self.determine_capture_info(possible_ancestor.info, capture_info);
439+
determine_capture_info(possible_ancestor.info, capture_info);
441440

442441
// Only one ancestor of the current place will be in the list.
443442
break;
@@ -500,60 +499,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
500499
self.tcx.has_attr(closure_def_id, sym::rustc_capture_analysis)
501500
}
502501

503-
/// Helper function to determine if we need to escalate CaptureKind from
504-
/// CaptureInfo A to B and returns the escalated CaptureInfo.
505-
/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)
506-
///
507-
/// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based
508-
/// on the `CaptureInfo` containing an associated expression id.
509-
///
510-
/// If both the CaptureKind and Expression are considered to be equivalent,
511-
/// then `CaptureInfo` A is preferred.
512-
fn determine_capture_info(
513-
&self,
514-
capture_info_a: ty::CaptureInfo<'tcx>,
515-
capture_info_b: ty::CaptureInfo<'tcx>,
516-
) -> ty::CaptureInfo<'tcx> {
517-
// If the capture kind is equivalent then, we don't need to escalate and can compare the
518-
// expressions.
519-
let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
520-
(ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => true,
521-
(ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => {
522-
ref_a.kind == ref_b.kind
523-
}
524-
_ => false,
525-
};
526-
527-
if eq_capture_kind {
528-
match (capture_info_a.expr_id, capture_info_b.expr_id) {
529-
(Some(_), _) | (None, None) => capture_info_a,
530-
(None, Some(_)) => capture_info_b,
531-
}
532-
} else {
533-
match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
534-
(ty::UpvarCapture::ByValue(_), _) => capture_info_a,
535-
(_, ty::UpvarCapture::ByValue(_)) => capture_info_b,
536-
(ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => {
537-
match (ref_a.kind, ref_b.kind) {
538-
// Take LHS:
539-
(ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow)
540-
| (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a,
541-
542-
// Take RHS:
543-
(ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow)
544-
| (ty::UniqueImmBorrow, ty::MutBorrow) => capture_info_b,
545-
546-
(ty::ImmBorrow, ty::ImmBorrow)
547-
| (ty::UniqueImmBorrow, ty::UniqueImmBorrow)
548-
| (ty::MutBorrow, ty::MutBorrow) => {
549-
bug!("Expected unequal capture kinds");
550-
}
551-
}
552-
}
553-
}
554-
}
555-
}
556-
557502
fn log_closure_capture_info(
558503
&self,
559504
closure_def_id: rustc_hir::def_id::DefId,
@@ -617,8 +562,9 @@ struct InferBorrowKind<'a, 'tcx> {
617562
// variable access that caused us to do so.
618563
current_origin: Option<(Span, Symbol)>,
619564

620-
/// For each Place that we access, we track the minimal kind of
565+
/// For each Place that is captured by the closure, we track the minimal kind of
621566
/// access we need (ref, ref mut, move, etc) and the expression that resulted in such access.
567+
///
622568
/// Consider closure where s.str1 is captured via an ImmutableBorrow and
623569
/// s.str2 via a MutableBorrow
624570
///
@@ -686,7 +632,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
686632
};
687633

688634
let curr_info = self.capture_information[&place_with_id.place];
689-
let updated_info = self.fcx.determine_capture_info(curr_info, capture_info);
635+
let updated_info = determine_capture_info(curr_info, capture_info);
690636

691637
self.capture_information[&place_with_id.place] = updated_info;
692638
}
@@ -804,7 +750,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
804750
expr_id: Some(diag_expr_id),
805751
capture_kind: ty::UpvarCapture::ByRef(new_upvar_borrow),
806752
};
807-
let updated_info = self.fcx.determine_capture_info(curr_capture_info, capture_info);
753+
let updated_info = determine_capture_info(curr_capture_info, capture_info);
808754
self.capture_information[&place_with_id.place] = updated_info;
809755
};
810756
}
@@ -859,14 +805,14 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
859805
if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
860806
assert_eq!(self.closure_def_id.expect_local(), upvar_id.closure_expr_id);
861807

862-
debug!("Capturing new place {:?}", place_with_id);
863-
864808
let capture_kind =
865809
self.fcx.init_capture_kind(self.capture_clause, upvar_id, self.closure_span);
866810

867811
let expr_id = Some(diag_expr_id);
868812
let capture_info = ty::CaptureInfo { expr_id, capture_kind };
869813

814+
debug!("Capturing new place {:?}, capture_info={:?}", place_with_id, capture_info);
815+
870816
self.capture_information.insert(place_with_id.place.clone(), capture_info);
871817
} else {
872818
debug!("Not upvar: {:?}", place_with_id);
@@ -964,6 +910,92 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
964910
tcx.hir().name(var_hir_id)
965911
}
966912

913+
/// Helper function to determine if we need to escalate CaptureKind from
914+
/// CaptureInfo A to B and returns the escalated CaptureInfo.
915+
/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)
916+
///
917+
/// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based
918+
/// on the `CaptureInfo` containing an associated expression id.
919+
///
920+
/// If both the CaptureKind and Expression are considered to be equivalent,
921+
/// then `CaptureInfo` A is preferred. This can be useful in cases where we want to priortize
922+
/// expressions reported back to the user as part of diagnostics based on which appears earlier
923+
/// in the closure. This can be acheived simply by calling
924+
/// `determine_capture_info(existing_info, current_info)`. This works out because the
925+
/// expressions that occur earlier in the closure body than the current expression are processed before.
926+
/// Consider the following example
927+
/// ```rust
928+
/// let mut p: Point { x: 10, y: 10 };
929+
///
930+
/// let c = || {
931+
/// p.x += 10;
932+
/// // ^ E1 ^
933+
/// // ...
934+
/// // More code
935+
/// // ...
936+
/// p.x += 10; // E2
937+
/// // ^ E2 ^
938+
/// }
939+
/// ```
940+
/// `CaptureKind` associated with both `E1` and `E2` will be ByRef(MutBorrow),
941+
/// and both have an expression associated, however for diagnostics we prfer reporting
942+
/// `E1` since it appears earlier in the closure body. When `E2` is being processed we
943+
/// would've already handled `E1`, and have an existing capture_information for it.
944+
/// Calling `determine_capture_info(existing_info_e1, current_info_e2)` will return
945+
/// `existing_info_e1` in this case, allowing us to point to `E1` in case of diagnostics.
946+
fn determine_capture_info(
947+
capture_info_a: ty::CaptureInfo<'tcx>,
948+
capture_info_b: ty::CaptureInfo<'tcx>,
949+
) -> ty::CaptureInfo<'tcx> {
950+
// If the capture kind is equivalent then, we don't need to escalate and can compare the
951+
// expressions.
952+
let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
953+
(ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => {
954+
// We don't need to worry about the spans being ignored here.
955+
//
956+
// The expr_id in capture_info corresponds to the span that is stored within
957+
// ByValue(span) and therefore it gets handled with priortizing based on
958+
// expressions below.
959+
true
960+
}
961+
(ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => {
962+
ref_a.kind == ref_b.kind
963+
}
964+
(ty::UpvarCapture::ByValue(_), _) | (ty::UpvarCapture::ByRef(_), _) => false,
965+
};
966+
967+
if eq_capture_kind {
968+
match (capture_info_a.expr_id, capture_info_b.expr_id) {
969+
(Some(_), _) | (None, None) => capture_info_a,
970+
(None, Some(_)) => capture_info_b,
971+
}
972+
} else {
973+
// We select the CaptureKind which ranks higher based the following priority order:
974+
// ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow
975+
match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
976+
(ty::UpvarCapture::ByValue(_), _) => capture_info_a,
977+
(_, ty::UpvarCapture::ByValue(_)) => capture_info_b,
978+
(ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => {
979+
match (ref_a.kind, ref_b.kind) {
980+
// Take LHS:
981+
(ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow)
982+
| (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a,
983+
984+
// Take RHS:
985+
(ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow)
986+
| (ty::UniqueImmBorrow, ty::MutBorrow) => capture_info_b,
987+
988+
(ty::ImmBorrow, ty::ImmBorrow)
989+
| (ty::UniqueImmBorrow, ty::UniqueImmBorrow)
990+
| (ty::MutBorrow, ty::MutBorrow) => {
991+
bug!("Expected unequal capture kinds");
992+
}
993+
}
994+
}
995+
}
996+
}
997+
}
998+
967999
/// Determines the Ancestry relationship of Place A relative to Place B
9681000
///
9691001
/// `PlaceAncestryRelation::Ancestor` implies Place A is ancestor of Place B

0 commit comments

Comments
 (0)