Skip to content

Commit 52dba13

Browse files
jenniferwillslogmosierroxelo
authored andcommitted
Replace closures_captures and upvar_capture with closure_min_captures
make changes to liveness to use closure_min_captures use different span borrow check uses new structures rename to CapturedPlace stop using upvar_capture in regionck remove the bridge cleanup from rebase + remove the upvar_capture reference from mutability_errors.rs remove line from livenes test make our unused var checking more consistent update tests adding more warnings to the tests move is_ancestor_or_same_capture to rustc_middle/ty update names to reflect the closures add FIXME check that all captures are immutable borrows before returning add surrounding if statement like the original move var out of the loop and rename Co-authored-by: Logan Mosier <[email protected]> Co-authored-by: Roxane Fruytier <[email protected]>
1 parent 1705a7d commit 52dba13

File tree

14 files changed

+477
-243
lines changed

14 files changed

+477
-243
lines changed

compiler/rustc_middle/src/ty/closure.rs

+50
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,56 @@ impl CapturedPlace<'tcx> {
168168
base => bug!("Expected upvar, found={:?}", base),
169169
}
170170
}
171+
172+
/// Returns the `LocalDefId` of the closure that captureed this Place
173+
pub fn get_closure_local_def_id(&self) -> LocalDefId {
174+
match self.place.base {
175+
HirPlaceBase::Upvar(upvar_id) => upvar_id.closure_expr_id,
176+
base => bug!("expected upvar, found={:?}", base),
177+
}
178+
}
179+
180+
/// Return span pointing to use that resulted in selecting the current capture kind
181+
pub fn get_capture_kind_span(&self, tcx: TyCtxt<'tcx>) -> Span {
182+
if let Some(capture_kind_expr_id) = self.info.capture_kind_expr_id {
183+
tcx.hir().span(capture_kind_expr_id)
184+
} else if let Some(path_expr_id) = self.info.path_expr_id {
185+
tcx.hir().span(path_expr_id)
186+
} else {
187+
// Fallback on upvars mentioned if neither path or capture expr id is captured
188+
189+
// Safe to unwrap since we know this place is captured by the closure, therefore the closure must have upvars.
190+
tcx.upvars_mentioned(self.get_closure_local_def_id()).unwrap()
191+
[&self.get_root_variable()]
192+
.span
193+
}
194+
}
195+
}
196+
197+
/// Return true if the `proj_possible_ancestor` represents an ancestor path
198+
/// to `proj_capture` or `proj_possible_ancestor` is same as `proj_capture`,
199+
/// assuming they both start off of the same root variable.
200+
///
201+
/// **Note:** It's the caller's responsibility to ensure that both lists of projections
202+
/// start off of the same root variable.
203+
///
204+
/// Eg: 1. `foo.x` which is represented using `projections=[Field(x)]` is an ancestor of
205+
/// `foo.x.y` which is represented using `projections=[Field(x), Field(y)]`.
206+
/// Note both `foo.x` and `foo.x.y` start off of the same root variable `foo`.
207+
/// 2. Since we only look at the projections here function will return `bar.x` as an a valid
208+
/// ancestor of `foo.x.y`. It's the caller's responsibility to ensure that both projections
209+
/// list are being applied to the same root variable.
210+
pub fn is_ancestor_or_same_capture(
211+
proj_possible_ancestor: &[HirProjectionKind],
212+
proj_capture: &[HirProjectionKind],
213+
) -> bool {
214+
// We want to make sure `is_ancestor_or_same_capture("x.0.0", "x.0")` to return false.
215+
// Therefore we can't just check if all projections are same in the zipped iterator below.
216+
if proj_possible_ancestor.len() > proj_capture.len() {
217+
return false;
218+
}
219+
220+
proj_possible_ancestor.iter().zip(proj_capture).all(|(a, b)| a == b)
171221
}
172222

173223
/// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move)

compiler/rustc_middle/src/ty/context.rs

+2-35
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@ use rustc_attr as attr;
3030
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
3131
use rustc_data_structures::profiling::SelfProfilerRef;
3232
use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap};
33-
use rustc_data_structures::stable_hasher::{
34-
hash_stable_hashmap, HashStable, StableHasher, StableVec,
35-
};
33+
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableVec};
3634
use rustc_data_structures::steal::Steal;
3735
use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal};
3836
use rustc_errors::ErrorReported;
@@ -386,9 +384,6 @@ pub struct TypeckResults<'tcx> {
386384
/// <https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions>
387385
pat_adjustments: ItemLocalMap<Vec<Ty<'tcx>>>,
388386

389-
/// Borrows
390-
pub upvar_capture_map: ty::UpvarCaptureMap<'tcx>,
391-
392387
/// Records the reasons that we picked the kind of each closure;
393388
/// not all closures are present in the map.
394389
closure_kind_origins: ItemLocalMap<(Span, HirPlace<'tcx>)>,
@@ -424,12 +419,6 @@ pub struct TypeckResults<'tcx> {
424419
/// by this function.
425420
pub concrete_opaque_types: FxHashMap<DefId, ResolvedOpaqueTy<'tcx>>,
426421

427-
/// Given the closure ID this map provides the list of UpvarIDs used by it.
428-
/// The upvarID contains the HIR node ID and it also contains the full path
429-
/// leading to the member of the struct or tuple that is used instead of the
430-
/// entire variable.
431-
pub closure_captures: ty::UpvarListMap,
432-
433422
/// Tracks the minimum captures required for a closure;
434423
/// see `MinCaptureInformationMap` for more details.
435424
pub closure_min_captures: ty::MinCaptureInformationMap<'tcx>,
@@ -482,15 +471,13 @@ impl<'tcx> TypeckResults<'tcx> {
482471
adjustments: Default::default(),
483472
pat_binding_modes: Default::default(),
484473
pat_adjustments: Default::default(),
485-
upvar_capture_map: Default::default(),
486474
closure_kind_origins: Default::default(),
487475
liberated_fn_sigs: Default::default(),
488476
fru_field_types: Default::default(),
489477
coercion_casts: Default::default(),
490478
used_trait_imports: Lrc::new(Default::default()),
491479
tainted_by_errors: None,
492480
concrete_opaque_types: Default::default(),
493-
closure_captures: Default::default(),
494481
closure_min_captures: Default::default(),
495482
closure_fake_reads: Default::default(),
496483
generator_interior_types: ty::Binder::dummy(Default::default()),
@@ -675,10 +662,6 @@ impl<'tcx> TypeckResults<'tcx> {
675662
.flatten()
676663
}
677664

678-
pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> ty::UpvarCapture<'tcx> {
679-
self.upvar_capture_map[&upvar_id]
680-
}
681-
682665
pub fn closure_kind_origins(&self) -> LocalTableInContext<'_, (Span, HirPlace<'tcx>)> {
683666
LocalTableInContext { hir_owner: self.hir_owner, data: &self.closure_kind_origins }
684667
}
@@ -722,7 +705,7 @@ impl<'tcx> TypeckResults<'tcx> {
722705
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
723706
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
724707
let ty::TypeckResults {
725-
hir_owner,
708+
hir_owner: _,
726709
ref type_dependent_defs,
727710
ref field_indices,
728711
ref user_provided_types,
@@ -732,17 +715,13 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
732715
ref adjustments,
733716
ref pat_binding_modes,
734717
ref pat_adjustments,
735-
ref upvar_capture_map,
736718
ref closure_kind_origins,
737719
ref liberated_fn_sigs,
738720
ref fru_field_types,
739-
740721
ref coercion_casts,
741-
742722
ref used_trait_imports,
743723
tainted_by_errors,
744724
ref concrete_opaque_types,
745-
ref closure_captures,
746725
ref closure_min_captures,
747726
ref closure_fake_reads,
748727
ref generator_interior_types,
@@ -759,17 +738,6 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
759738
adjustments.hash_stable(hcx, hasher);
760739
pat_binding_modes.hash_stable(hcx, hasher);
761740
pat_adjustments.hash_stable(hcx, hasher);
762-
hash_stable_hashmap(hcx, hasher, upvar_capture_map, |up_var_id, hcx| {
763-
let ty::UpvarId { var_path, closure_expr_id } = *up_var_id;
764-
765-
assert_eq!(var_path.hir_id.owner, hir_owner);
766-
767-
(
768-
hcx.local_def_path_hash(var_path.hir_id.owner),
769-
var_path.hir_id.local_id,
770-
hcx.local_def_path_hash(closure_expr_id),
771-
)
772-
});
773741

774742
closure_kind_origins.hash_stable(hcx, hasher);
775743
liberated_fn_sigs.hash_stable(hcx, hasher);
@@ -778,7 +746,6 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckResults<'tcx> {
778746
used_trait_imports.hash_stable(hcx, hasher);
779747
tainted_by_errors.hash_stable(hcx, hasher);
780748
concrete_opaque_types.hash_stable(hcx, hasher);
781-
closure_captures.hash_stable(hcx, hasher);
782749
closure_min_captures.hash_stable(hcx, hasher);
783750
closure_fake_reads.hash_stable(hcx, hasher);
784751
generator_interior_types.hash_stable(hcx, hasher);

compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs

+22-18
Original file line numberDiff line numberDiff line change
@@ -388,10 +388,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
388388
// so it's safe to call `expect_local`.
389389
//
390390
// We know the field exists so it's safe to call operator[] and `unwrap` here.
391-
let (&var_id, _) =
392-
self.infcx.tcx.typeck(def_id.expect_local()).closure_captures[&def_id]
393-
.get_index(field.index())
394-
.unwrap();
391+
let var_id = self
392+
.infcx
393+
.tcx
394+
.typeck(def_id.expect_local())
395+
.closure_min_captures_flattened(def_id)
396+
.nth(field.index())
397+
.unwrap()
398+
.get_root_variable();
395399

396400
self.infcx.tcx.hir().name(var_id).to_string()
397401
}
@@ -966,35 +970,35 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
966970
let expr = &self.infcx.tcx.hir().expect_expr(hir_id).kind;
967971
debug!("closure_span: hir_id={:?} expr={:?}", hir_id, expr);
968972
if let hir::ExprKind::Closure(.., body_id, args_span, _) = expr {
969-
for (upvar_hir_id, place) in
970-
self.infcx.tcx.typeck(def_id.expect_local()).closure_captures[&def_id]
971-
.keys()
972-
.zip(places)
973+
for (captured_place, place) in self
974+
.infcx
975+
.tcx
976+
.typeck(def_id.expect_local())
977+
.closure_min_captures_flattened(def_id)
978+
.zip(places)
973979
{
974-
let span = self.infcx.tcx.upvars_mentioned(local_did)?[upvar_hir_id].span;
980+
let upvar_hir_id = captured_place.get_root_variable();
981+
//FIXME(project-rfc-2229#8): Use better span from captured_place
982+
let span = self.infcx.tcx.upvars_mentioned(local_did)?[&upvar_hir_id].span;
975983
match place {
976984
Operand::Copy(place) | Operand::Move(place)
977985
if target_place == place.as_ref() =>
978986
{
979987
debug!("closure_span: found captured local {:?}", place);
980988
let body = self.infcx.tcx.hir().body(*body_id);
981989
let generator_kind = body.generator_kind();
982-
let upvar_id = ty::UpvarId {
983-
var_path: ty::UpvarPath { hir_id: *upvar_hir_id },
984-
closure_expr_id: local_did,
985-
};
986990

987991
// If we have a more specific span available, point to that.
988992
// We do this even though this span might be part of a borrow error
989993
// message rather than a move error message. Our goal is to point
990994
// to a span that shows why the upvar is used in the closure,
991995
// so a move-related span is as good as any (and potentially better,
992996
// if the overall error is due to a move of the upvar).
993-
let usage_span =
994-
match self.infcx.tcx.typeck(local_did).upvar_capture(upvar_id) {
995-
ty::UpvarCapture::ByValue(Some(span)) => span,
996-
_ => span,
997-
};
997+
998+
let usage_span = match captured_place.info.capture_kind {
999+
ty::UpvarCapture::ByValue(Some(span)) => span,
1000+
_ => span,
1001+
};
9981002
return Some((*args_span, generator_kind, usage_span));
9991003
}
10001004
_ => {}

compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs

+46-16
Original file line numberDiff line numberDiff line change
@@ -510,24 +510,54 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
510510
the_place_err: PlaceRef<'tcx>,
511511
err: &mut DiagnosticBuilder<'_>,
512512
) {
513-
let id = id.expect_local();
514-
let tables = tcx.typeck(id);
515-
let hir_id = tcx.hir().local_def_id_to_hir_id(id);
516-
if let Some((span, place)) = tables.closure_kind_origins().get(hir_id) {
517-
let reason = if let PlaceBase::Upvar(upvar_id) = place.base {
518-
let upvar = ty::place_to_string_for_capture(tcx, place);
519-
match tables.upvar_capture(upvar_id) {
520-
ty::UpvarCapture::ByRef(ty::UpvarBorrow {
521-
kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
522-
..
523-
}) => {
524-
format!("mutable borrow of `{}`", upvar)
525-
}
526-
ty::UpvarCapture::ByValue(_) => {
527-
format!("possible mutation of `{}`", upvar)
513+
let closure_local_def_id = id.expect_local();
514+
let tables = tcx.typeck(closure_local_def_id);
515+
let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_local_def_id);
516+
if let Some((span, closure_kind_origin)) =
517+
&tables.closure_kind_origins().get(closure_hir_id)
518+
{
519+
let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base {
520+
let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin);
521+
let root_hir_id = upvar_id.var_path.hir_id;
522+
// we have a origin for this closure kind starting at this root variable so it's safe to unwrap here
523+
let captured_places = tables.closure_min_captures[id].get(&root_hir_id).unwrap();
524+
525+
let origin_projection = closure_kind_origin
526+
.projections
527+
.iter()
528+
.map(|proj| proj.kind)
529+
.collect::<Vec<_>>();
530+
let mut capture_reason = String::new();
531+
for captured_place in captured_places {
532+
let captured_place_kinds = captured_place
533+
.place
534+
.projections
535+
.iter()
536+
.map(|proj| proj.kind)
537+
.collect::<Vec<_>>();
538+
if rustc_middle::ty::is_ancestor_or_same_capture(
539+
&captured_place_kinds,
540+
&origin_projection,
541+
) {
542+
match captured_place.info.capture_kind {
543+
ty::UpvarCapture::ByRef(ty::UpvarBorrow {
544+
kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
545+
..
546+
}) => {
547+
capture_reason = format!("mutable borrow of `{}`", upvar);
548+
}
549+
ty::UpvarCapture::ByValue(_) => {
550+
capture_reason = format!("possible mutation of `{}`", upvar);
551+
}
552+
_ => bug!("upvar `{}` borrowed, but not mutably", upvar),
553+
}
554+
break;
528555
}
529-
val => bug!("upvar `{}` borrowed, but not mutably: {:?}", upvar, val),
530556
}
557+
if capture_reason.is_empty() {
558+
bug!("upvar `{}` borrowed, but cannot find reason", upvar);
559+
}
560+
capture_reason
531561
} else {
532562
bug!("not an upvar")
533563
};

compiler/rustc_mir/src/interpret/validity.rs

+13-10
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ macro_rules! throw_validation_failure {
7777
///
7878
macro_rules! try_validation {
7979
($e:expr, $where:expr,
80-
$( $( $p:pat )|+ => { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? ),+ $(,)?
80+
$( $( $p:pat )|+ => { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? ),+ $(,)?
8181
) => {{
8282
match $e {
8383
Ok(x) => x,
@@ -244,17 +244,20 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
244244
// generators and closures.
245245
ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
246246
let mut name = None;
247-
if let Some(def_id) = def_id.as_local() {
248-
let tables = self.ecx.tcx.typeck(def_id);
249-
if let Some(upvars) = tables.closure_captures.get(&def_id.to_def_id()) {
247+
// FIXME this should be more descriptive i.e. CapturePlace instead of CapturedVar
248+
// https://github.com/rust-lang/project-rfc-2229/issues/46
249+
if let Some(local_def_id) = def_id.as_local() {
250+
let tables = self.ecx.tcx.typeck(local_def_id);
251+
if let Some(captured_place) =
252+
tables.closure_min_captures_flattened(*def_id).nth(field)
253+
{
250254
// Sometimes the index is beyond the number of upvars (seen
251255
// for a generator).
252-
if let Some((&var_hir_id, _)) = upvars.get_index(field) {
253-
let node = self.ecx.tcx.hir().get(var_hir_id);
254-
if let hir::Node::Binding(pat) = node {
255-
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
256-
name = Some(ident.name);
257-
}
256+
let var_hir_id = captured_place.get_root_variable();
257+
let node = self.ecx.tcx.hir().get(var_hir_id);
258+
if let hir::Node::Binding(pat) = node {
259+
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
260+
name = Some(ident.name);
258261
}
259262
}
260263
}

compiler/rustc_mir_build/src/build/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
881881
let hir_typeck_results = self.typeck_results;
882882

883883
// In analyze_closure() in upvar.rs we gathered a list of upvars used by a
884-
// indexed closure and we stored in a map called closure_captures in TypeckResults
884+
// indexed closure and we stored in a map called closure_min_captures in TypeckResults
885885
// with the closure's DefId. Here, we run through that vec of UpvarIds for
886886
// the given closure and use the necessary information to create upvar
887887
// debuginfo and to fill `self.upvar_mutbls`.

0 commit comments

Comments
 (0)