Skip to content

Commit 2624b3e

Browse files
committed
Improve diagnostics for Precise Capture
1 parent fcbd305 commit 2624b3e

14 files changed

+581
-31
lines changed

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -741,8 +741,20 @@ pub struct CapturedPlace<'tcx> {
741741
pub struct CaptureInfo<'tcx> {
742742
/// Expr Id pointing to use that resulted in selecting the current capture kind
743743
///
744+
/// Eg:
745+
/// ```rust,no_run
746+
/// let mut t = (0,1);
747+
///
748+
/// let c = || {
749+
/// println!("{}",t); // L1
750+
/// t.1 = 4; // L2
751+
/// };
752+
/// ```
753+
/// `capture_kind_expr_id` will point to the use on L2 and `path_expr_id` will point to the
754+
/// use on L1.
755+
///
744756
/// If the user doesn't enable feature `capture_disjoint_fields` (RFC 2229) then, it is
745-
/// possible that we don't see the use of a particular place resulting in expr_id being
757+
/// possible that we don't see the use of a particular place resulting in capture_kind_expr_id being
746758
/// None. In such case we fallback on uvpars_mentioned for span.
747759
///
748760
/// Eg:
@@ -756,7 +768,12 @@ pub struct CaptureInfo<'tcx> {
756768
///
757769
/// In this example, if `capture_disjoint_fields` is **not** set, then x will be captured,
758770
/// but we won't see it being used during capture analysis, since it's essentially a discard.
759-
pub expr_id: Option<hir::HirId>,
771+
pub capture_kind_expr_id: Option<hir::HirId>,
772+
/// Expr Id pointing to use that resulted the corresponding place being captured
773+
///
774+
/// See `capture_kind_expr_id` for example.
775+
///
776+
pub path_expr_id: Option<hir::HirId>,
760777

761778
/// Capture mode that was selected
762779
pub capture_kind: UpvarCapture<'tcx>,

compiler/rustc_typeck/src/check/upvar.rs

Lines changed: 124 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use rustc_infer::infer::UpvarRegion;
4242
use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, ProjectionKind};
4343
use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts};
4444
use rustc_span::sym;
45-
use rustc_span::{Span, Symbol};
45+
use rustc_span::{MultiSpan, Span, Symbol};
4646

4747
/// Describe the relationship between the paths of two places
4848
/// eg:
@@ -135,7 +135,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
135135

136136
let upvar_id = ty::UpvarId::new(var_hir_id, local_def_id);
137137
let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span);
138-
let info = ty::CaptureInfo { expr_id: None, capture_kind };
138+
let info = ty::CaptureInfo {
139+
capture_kind_expr_id: None,
140+
path_expr_id: None,
141+
capture_kind,
142+
};
139143

140144
capture_information.insert(place, info);
141145
}
@@ -298,8 +302,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
298302
if let Some(capture_kind) = upvar_capture_map.get(&upvar_id) {
299303
// upvar_capture_map only stores the UpvarCapture (CaptureKind),
300304
// so we create a fake capture info with no expression.
301-
let fake_capture_info =
302-
ty::CaptureInfo { expr_id: None, capture_kind: *capture_kind };
305+
let fake_capture_info = ty::CaptureInfo {
306+
capture_kind_expr_id: None,
307+
path_expr_id: None,
308+
capture_kind: *capture_kind,
309+
};
303310
determine_capture_info(fake_capture_info, capture_info).capture_kind
304311
} else {
305312
capture_info.capture_kind
@@ -349,20 +356,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
349356
///
350357
/// ```
351358
/// {
352-
/// Place(base: hir_id_s, projections: [], ....) -> (hir_id_L5, ByValue),
353-
/// Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> (hir_id_L2, ByRef(MutBorrow))
354-
/// Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> (hir_id_L3, ByRef(ImmutBorrow))
355-
/// Place(base: hir_id_p, projections: [], ...) -> (hir_id_L4, ByRef(ImmutBorrow))
359+
/// Place(base: hir_id_s, projections: [], ....) -> {
360+
/// capture_kind_expr: hir_id_L5,
361+
/// path_expr_id: hir_id_L5,
362+
/// capture_kind: ByValue
363+
/// },
364+
/// Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> {
365+
/// capture_kind_expr: hir_id_L2,
366+
/// path_expr_id: hir_id_L2,
367+
/// capture_kind: ByValue
368+
/// },
369+
/// Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> {
370+
/// capture_kind_expr: hir_id_L3,
371+
/// path_expr_id: hir_id_L3,
372+
/// capture_kind: ByValue
373+
/// },
374+
/// Place(base: hir_id_p, projections: [], ...) -> {
375+
/// capture_kind_expr: hir_id_L4,
376+
/// path_expr_id: hir_id_L4,
377+
/// capture_kind: ByValue
378+
/// },
356379
/// ```
357380
///
358381
/// After the min capture analysis, we get:
359382
/// ```
360383
/// {
361384
/// hir_id_s -> [
362-
/// Place(base: hir_id_s, projections: [], ....) -> (hir_id_L4, ByValue)
385+
/// Place(base: hir_id_s, projections: [], ....) -> {
386+
/// capture_kind_expr: hir_id_L5,
387+
/// path_expr_id: hir_id_L5,
388+
/// capture_kind: ByValue
389+
/// },
363390
/// ],
364391
/// hir_id_p -> [
365-
/// Place(base: hir_id_p, projections: [], ...) -> (hir_id_L2, ByRef(MutBorrow)),
392+
/// Place(base: hir_id_p, projections: [], ...) -> {
393+
/// capture_kind_expr: hir_id_L2,
394+
/// path_expr_id: hir_id_L4,
395+
/// capture_kind: ByValue
396+
/// },
366397
/// ],
367398
/// ```
368399
fn compute_min_captures(
@@ -415,8 +446,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
415446
// current place is ancestor of possible_descendant
416447
PlaceAncestryRelation::Ancestor => {
417448
descendant_found = true;
449+
let backup_path_expr_id = updated_capture_info.path_expr_id;
450+
418451
updated_capture_info =
419452
determine_capture_info(updated_capture_info, possible_descendant.info);
453+
454+
// we need to keep the ancestor's `path_expr_id`
455+
updated_capture_info.path_expr_id = backup_path_expr_id;
420456
false
421457
}
422458

@@ -431,9 +467,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
431467
// current place is descendant of possible_ancestor
432468
PlaceAncestryRelation::Descendant => {
433469
ancestor_found = true;
470+
let backup_path_expr_id = possible_ancestor.info.path_expr_id;
434471
possible_ancestor.info =
435472
determine_capture_info(possible_ancestor.info, capture_info);
436473

474+
// we need to keep the ancestor's `path_expr_id`
475+
possible_ancestor.info.path_expr_id = backup_path_expr_id;
476+
437477
// Only one ancestor of the current place will be in the list.
438478
break;
439479
}
@@ -508,7 +548,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
508548
let capture_str = construct_capture_info_string(self.tcx, place, capture_info);
509549
let output_str = format!("Capturing {}", capture_str);
510550

511-
let span = capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e));
551+
let span =
552+
capture_info.path_expr_id.map_or(closure_span, |e| self.tcx.hir().span(e));
512553
diag.span_note(span, &output_str);
513554
}
514555
diag.emit();
@@ -532,9 +573,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
532573
construct_capture_info_string(self.tcx, place, capture_info);
533574
let output_str = format!("Min Capture {}", capture_str);
534575

535-
let span =
536-
capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e));
537-
diag.span_note(span, &output_str);
576+
if capture.info.path_expr_id != capture.info.capture_kind_expr_id {
577+
let path_span = capture_info
578+
.path_expr_id
579+
.map_or(closure_span, |e| self.tcx.hir().span(e));
580+
let capture_kind_span = capture_info
581+
.capture_kind_expr_id
582+
.map_or(closure_span, |e| self.tcx.hir().span(e));
583+
584+
let mut multi_span: MultiSpan =
585+
MultiSpan::from_spans(vec![path_span, capture_kind_span]);
586+
587+
let capture_kind_label =
588+
construct_capture_kind_reason_string(self.tcx, place, capture_info);
589+
let path_label = construct_path_string(self.tcx, place);
590+
591+
multi_span.push_span_label(path_span, path_label);
592+
multi_span.push_span_label(capture_kind_span, capture_kind_label);
593+
594+
diag.span_note(multi_span, &output_str);
595+
} else {
596+
let span = capture_info
597+
.path_expr_id
598+
.map_or(closure_span, |e| self.tcx.hir().span(e));
599+
600+
diag.span_note(span, &output_str);
601+
};
538602
}
539603
}
540604
diag.emit();
@@ -632,7 +696,8 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
632696
);
633697

634698
let capture_info = ty::CaptureInfo {
635-
expr_id: Some(diag_expr_id),
699+
capture_kind_expr_id: Some(diag_expr_id),
700+
path_expr_id: Some(diag_expr_id),
636701
capture_kind: ty::UpvarCapture::ByValue(Some(usage_span)),
637702
};
638703

@@ -752,7 +817,8 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
752817
let new_upvar_borrow = ty::UpvarBorrow { kind, region: curr_upvar_borrow.region };
753818

754819
let capture_info = ty::CaptureInfo {
755-
expr_id: Some(diag_expr_id),
820+
capture_kind_expr_id: Some(diag_expr_id),
821+
path_expr_id: Some(diag_expr_id),
756822
capture_kind: ty::UpvarCapture::ByRef(new_upvar_borrow),
757823
};
758824
let updated_info = determine_capture_info(curr_capture_info, capture_info);
@@ -814,7 +880,11 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
814880
self.fcx.init_capture_kind(self.capture_clause, upvar_id, self.closure_span);
815881

816882
let expr_id = Some(diag_expr_id);
817-
let capture_info = ty::CaptureInfo { expr_id, capture_kind };
883+
let capture_info = ty::CaptureInfo {
884+
capture_kind_expr_id: expr_id,
885+
path_expr_id: expr_id,
886+
capture_kind,
887+
};
818888

819889
debug!("Capturing new place {:?}, capture_info={:?}", place_with_id, capture_info);
820890

@@ -880,11 +950,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
880950
}
881951
}
882952

883-
fn construct_capture_info_string(
884-
tcx: TyCtxt<'_>,
885-
place: &Place<'tcx>,
886-
capture_info: &ty::CaptureInfo<'tcx>,
887-
) -> String {
953+
fn construct_place_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String {
888954
let variable_name = match place.base {
889955
PlaceBase::Upvar(upvar_id) => var_name(tcx, upvar_id.var_path.hir_id).to_string(),
890956
_ => bug!("Capture_information should only contain upvars"),
@@ -904,11 +970,42 @@ fn construct_capture_info_string(
904970
projections_str.push_str(proj.as_str());
905971
}
906972

973+
format!("{}[{}]", variable_name, projections_str)
974+
}
975+
976+
fn construct_capture_kind_reason_string(
977+
tcx: TyCtxt<'_>,
978+
place: &Place<'tcx>,
979+
capture_info: &ty::CaptureInfo<'tcx>,
980+
) -> String {
981+
let place_str = construct_place_string(tcx, &place);
982+
907983
let capture_kind_str = match capture_info.capture_kind {
908984
ty::UpvarCapture::ByValue(_) => "ByValue".into(),
909985
ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind),
910986
};
911-
format!("{}[{}] -> {}", variable_name, projections_str, capture_kind_str)
987+
988+
format!("{} captured as {} here", place_str, capture_kind_str)
989+
}
990+
991+
fn construct_path_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String {
992+
let place_str = construct_place_string(tcx, &place);
993+
994+
format!("{} used here", place_str)
995+
}
996+
997+
fn construct_capture_info_string(
998+
tcx: TyCtxt<'_>,
999+
place: &Place<'tcx>,
1000+
capture_info: &ty::CaptureInfo<'tcx>,
1001+
) -> String {
1002+
let place_str = construct_place_string(tcx, &place);
1003+
1004+
let capture_kind_str = match capture_info.capture_kind {
1005+
ty::UpvarCapture::ByValue(_) => "ByValue".into(),
1006+
ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind),
1007+
};
1008+
format!("{} -> {}", place_str, capture_kind_str)
9121009
}
9131010

9141011
fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
@@ -920,7 +1017,9 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
9201017
/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)
9211018
///
9221019
/// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based
923-
/// on the `CaptureInfo` containing an associated expression id.
1020+
/// on the `CaptureInfo` containing an associated `capture_kind_expr_id`.
1021+
///
1022+
/// It is the caller's duty to figure out which path_expr_id to use.
9241023
///
9251024
/// If both the CaptureKind and Expression are considered to be equivalent,
9261025
/// then `CaptureInfo` A is preferred. This can be useful in cases where we want to priortize
@@ -971,7 +1070,7 @@ fn determine_capture_info(
9711070
};
9721071

9731072
if eq_capture_kind {
974-
match (capture_info_a.expr_id, capture_info_b.expr_id) {
1073+
match (capture_info_a.capture_kind_expr_id, capture_info_b.capture_kind_expr_id) {
9751074
(Some(_), _) | (None, None) => capture_info_a,
9761075
(None, Some(_)) => capture_info_b,
9771076
}

compiler/rustc_typeck/src/check/writeback.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
348348
let min_list_wb = min_list
349349
.iter()
350350
.map(|captured_place| {
351-
let locatable = captured_place.info.expr_id.unwrap_or(
351+
let locatable = captured_place.info.path_expr_id.unwrap_or(
352352
self.tcx().hir().local_def_id_to_hir_id(closure_def_id.expect_local()),
353353
);
354354

compiler/rustc_typeck/src/expr_use_visitor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
630630
PlaceBase::Local(*var_hir_id)
631631
};
632632
let place_with_id = PlaceWithHirId::new(
633-
capture_info.expr_id.unwrap_or(closure_expr.hir_id),
633+
capture_info.path_expr_id.unwrap_or(closure_expr.hir_id),
634634
place.base_ty,
635635
place_base,
636636
place.projections.clone(),
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
#![feature(capture_disjoint_fields)]
3+
//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
4+
//~| NOTE: `#[warn(incomplete_features)]` on by default
5+
//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
6+
#![feature(rustc_attrs)]
7+
8+
#[derive(Debug)]
9+
struct Point {
10+
x: i32,
11+
y: i32,
12+
}
13+
14+
fn main() {
15+
let p = Point { x: 10, y: 10 };
16+
let q = Point { x: 10, y: 10 };
17+
18+
let c = #[rustc_capture_analysis]
19+
//~^ ERROR: attributes on expressions are experimental
20+
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
21+
|| {
22+
//~^ First Pass analysis includes:
23+
//~| Min Capture analysis includes:
24+
println!("{:?}", p);
25+
//~^ NOTE: Capturing p[] -> ImmBorrow
26+
//~| NOTE: Min Capture p[] -> ImmBorrow
27+
println!("{:?}", p.x);
28+
//~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow
29+
30+
println!("{:?}", q.x);
31+
//~^ NOTE: Capturing q[(0, 0)] -> ImmBorrow
32+
println!("{:?}", q);
33+
//~^ NOTE: Capturing q[] -> ImmBorrow
34+
//~| NOTE: Min Capture q[] -> ImmBorrow
35+
};
36+
}

0 commit comments

Comments
 (0)