8000 RFC-2229: Implement Precise Capture Analysis by arora-aman · Pull Request #78801 · rust-lang/rust · GitHub
[go: up one dir, main page]

Skip to content

RFC-2229: Implement Precise Capture Analysis #78801

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Nov 17, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Reduce verbosity of capture analysis logs
Co-authored-by: Jenny Wills <wills.jenniferg@gmail.com>
Co-authored-by: Aman Arora <me@aman-arora.com>
  • Loading branch information
3 people committed Nov 11, 2020
commit 825e9e45d14694d23fde29fdab2b02d9973a4eb3
112 changes: 79 additions & 33 deletions compiler/rustc_typeck/src/check/upvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,6 @@ use rustc_span::{Span, Symbol};

use std::env;

macro_rules! log_capture_analysis {
($fcx:expr, $closure_def_id:expr, $fmt:literal) => {
if $fcx.should_log_capture_analysis($closure_def_id) {
print!("For closure={:?}: ", $closure_def_id);
println!($fmt);
}
};

($fcx:expr, $closure_def_id:expr, $fmt:literal, $($args:expr),*) => {
if $fcx.should_log_capture_analysis($closure_def_id) {
print!("For closure={:?}: ", $closure_def_id);
println!($fmt, $($args),*);
}
};
}

/// Describe the relationship between the paths of two places
/// eg:
/// - foo is ancestor of foo.bar.baz
Expand Down Expand Up @@ -144,9 +128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

let mut capture_information = FxIndexMap::<Place<'tcx>, ty::CaptureInfo<'tcx>>::default();
if self.tcx.features().capture_disjoint_fields || matches!(env::var("SG_NEW"), Ok(_)) {
log_capture_analysis!(self, closure_def_id, "Using new-style capture analysis");
} else {
log_capture_analysis!(self, closure_def_id, "Using old-style capture analysis");
if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
for (&var_hir_id, _) in upvars.iter() {
let place = self.place_for_root_variable(local_def_id, var_hir_id);
Expand Down Expand Up @@ -182,12 +164,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
.consume_body(body);

log_capture_analysis!(
self,
closure_def_id,
"capture information: {:#?}",
delegate.capture_information
debug!(
"For closure={:?}, capture_information={:#?}",
closure_def_id, delegate.capture_information
);
self.log_closure_capture_info(closure_def_id, &delegate.capture_information, span);

if let Some(closure_substs) = infer_kind {
// Unify the (as yet unbound) type variable in the closure
Expand All @@ -206,6 +187,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

self.compute_min_captures(closure_def_id, delegate);
self.log_closure_min_capture_info(closure_def_id, span);

self.set_closure_captures(closure_def_id);

// Now that we've analyzed the closure, we know how each
Expand Down Expand Up @@ -333,10 +316,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
debug!(
"For closure_def_id={:?}, set_closure_captures={:#?}",
closure_def_id, closure_captures
);
debug!("For closure_def_id={:?}, closure_captures={:#?}", closure_def_id, closure_captures);
debug!(
"For closure_def_id={:?}, upvar_capture_map={:#?}",
closure_def_id, upvar_capture_map
Expand Down Expand Up @@ -478,12 +458,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

log_capture_analysis!(
self,
closure_def_id,
"min_captures={:#?}",
root_var_min_capture_list
);
debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list);

if !root_var_min_capture_list.is_empty() {
self.typeck_results
Expand Down Expand Up @@ -581,6 +556,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}

fn log_closure_capture_info(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so this is logging every capture...

Copy link
Member Author
@arora-aman arora-aman Nov 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed the function to express the intent more clearly

&self,
closure_def_id: rustc_hir::def_id::DefId,
capture_information: &FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>,
closure_span: Span,
) {
if self.should_log_capture_analysis(closure_def_id) {
for (place, capture_info) in capture_information {
let capture_str = construct_capture_info_string(self.tcx, place, capture_info);
let output_str = format!("Capturing {}", capture_str);

let span = capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e));
self.tcx.sess.span_err(span, &output_str);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thought I have for how to make this clearer is to add a "note" that indicates which closure this is captured by. Something like:

let mut diag = self.tcx.sess.struct_span_err(span, &output);
diag.span_note(closure_span, "captured by this closure");
diag.emit();

}
}
}

fn log_closure_min_capture_info(&self, closure_def_id: DefId, closure_span: Span) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

....and this is logging the minimum captures ...?

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)
{
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);
let output_str = format!("Min Capture {}", capture_str);

let span =
capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e));
self.tcx.sess.span_err(span, &output_str);
}
}
}
}
}
}

struct InferBorrowKind<'a, 'tcx> {
Expand Down Expand Up @@ -917,6 +932,37 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
}
}

fn construct_capture_info_string(
tcx: TyCtxt<'_>,
place: &Place<'tcx>,
capture_info: &ty::CaptureInfo<'tcx>,
) -> String {
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 {
projections_str.push_str(",");
}
projections_str.push_str(proj.as_str());
}

let capture_kind_str = match capture_info.capture_kind {
ty::UpvarCapture::ByValue(_) => "ByValue".into(),
ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind),
};
format!("{}[{}] -> {}", variable_name, projections_str, capture_kind_str)
}

fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
tcx.hir().name(var_hir_id)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ fn main() {
//~^ ERROR: attributes on expressions are experimental
|| {
m[0] += 10;
//~^ ERROR: Capturing m[] -> MutBorrow
//~^^ ERROR: Min Capture m[] -> MutBorrow
m[1] += 40;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)]
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information

error: aborting due to previous error; 1 warning emitted
error: Capturing m[] -> MutBorrow
--> $DIR/arrays-completely-captured.rs:12:9
|
LL | m[0] += 10;
| ^

error: Min Capture m[] -> MutBorrow
--> $DIR/arrays-completely-captured.rs:12:9
|
LL | m[0] += 10;
| ^

error: aborting due to 3 previous errors; 1 warning emitted

For more information about this error, try `rustc --explain E0658`.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ fn main() {
//~^ ERROR: attributes on expressions are experimental
|| {
println!("{}", p.x);
//~^ ERROR: Capturing p[(0, 0)] -> ImmBorrow
//~^^ ERROR: Min Capture p[(0, 0)] -> ImmBorrow
};

// `c` should only capture `p.x`, therefore mutating `p.y` is allowed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ LL | #![feature(capture_disjoint_fields)]
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information

error: aborting due to previous error; 1 warning emitted
error: Capturing p[(0, 0)] -> ImmBorrow
--> $DIR/capture-disjoint-field-struct.rs:18:24
|
LL | println!("{}", p.x);
| ^^^

error: Min Capture p[(0, 0)] -> ImmBorrow
--> $DIR/capture-disjoint-field-struct.rs:18:24
|
LL | println!("{}", p.x);
| ^^^

error: aborting due to 3 previous errors; 1 warning emitted

For more information about this error, try `rustc --explain E0658`.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ fn main() {
//~^ ERROR: attributes on expressions are experimental
|| {
println!("{}", t.0);
//~^ ERROR: Capturing t[(0, 0)] -> ImmBorrow
//~^^ ERROR: Min Capture t[(0, 0)] -> ImmBorrow
};

// `c` only captures t.0, therefore mutating t.1 is allowed.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/capture-disjoint-field-tuple.rs:8:13
--> $DIR/capture-disjoint-field-tuple.rs:10:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -8,14 +8,26 @@ LL | let c = #[rustc_capture_analysis]
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable

warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/capture-disjoint-field-tuple.rs:1:12
--> $DIR/capture-disjoint-field-tuple.rs:3:12
|
LL | #![feature(capture_disjoint_fields)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information

error: aborting due to previous error; 1 warning emitted
error: Capturing t[(0, 0)] -> ImmBorrow
--> $DIR/capture-disjoint-field-tuple.rs:13:24
|
LL | println!("{}", t.0);
| ^^^

error: Min Capture t[(0, 0)] -> ImmBorrow
--> $DIR/capture-disjoint-field-tuple.rs:13:24
|
LL | println!("{}", t.0);
| ^^^

error: aborting due to 3 previous errors; 1 warning emitted

For more information about this error, try `rustc --explain E0658`.
Loading
0