Skip to content

Commit 314fe4d

Browse files
authored
Rollup merge of #104239 - b-naber:sccs-info, r=jackh726
Better debug logs for borrowck constraint graph It's really cumbersome to work with `RegionVar`s when trying to debug borrowck code or when trying to understand how the borrowchecker works. This PR collects some region information (behind `cfg(debug_assertions)`) for created `RegionVar`s (NLL region vars, this PR doesn't touch canonicalization) and prints the nodes and edges of the strongly connected constraints graph using representatives that use that region information (either lifetime names, locations in MIR or spans).
2 parents f4c7596 + 8252a6e commit 314fe4d

File tree

15 files changed

+438
-68
lines changed

15 files changed

+438
-68
lines changed

compiler/rustc_borrowck/src/constraints/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub(crate) mod graph;
1717
/// constraints of the form `R1: R2`. Each constraint is identified by
1818
/// a unique `OutlivesConstraintIndex` and you can index into the set
1919
/// (`constraint_set[i]`) to access the constraint details.
20-
#[derive(Clone, Default)]
20+
#[derive(Clone, Debug, Default)]
2121
pub(crate) struct OutlivesConstraintSet<'tcx> {
2222
outlives: IndexVec<OutlivesConstraintIndex, OutlivesConstraint<'tcx>>,
2323
}

compiler/rustc_borrowck/src/lib.rs

+89-9
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ use rustc_hir as hir;
2525
use rustc_hir::def_id::LocalDefId;
2626
use rustc_index::bit_set::ChunkedBitSet;
2727
use rustc_index::vec::IndexVec;
28-
use rustc_infer::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt};
28+
use rustc_infer::infer::{
29+
DefiningAnchor, InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
30+
};
2931
use rustc_middle::mir::{
3032
traversal, Body, ClearCrossCrate, Local, Location, Mutability, NonDivergingIntrinsic, Operand,
3133
Place, PlaceElem, PlaceRef, VarDebugInfoContents,
@@ -43,6 +45,7 @@ use smallvec::SmallVec;
4345
use std::cell::OnceCell;
4446
use std::cell::RefCell;
4547
use std::collections::BTreeMap;
48+
use std::ops::Deref;
4649
use std::rc::Rc;
4750

4851
use rustc_mir_dataflow::impls::{
@@ -94,6 +97,7 @@ use nll::{PoloniusOutput, ToRegionVid};
9497
use place_ext::PlaceExt;
9598
use places_conflict::{places_conflict, PlaceConflictBias};
9699
use region_infer::RegionInferenceContext;
100+
use renumber::RegionCtxt;
97101

98102
// FIXME(eddyb) perhaps move this somewhere more centrally.
99103
#[derive(Debug)]
@@ -167,10 +171,10 @@ fn do_mir_borrowck<'tcx>(
167171
return_body_with_facts: bool,
168172
) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
169173
let def = input_body.source.with_opt_param().as_local().unwrap();
170-
171174
debug!(?def);
172175

173176
let tcx = infcx.tcx;
177+
let infcx = BorrowckInferCtxt::new(infcx);
174178
let param_env = tcx.param_env(def.did);
175179

176180
let mut local_names = IndexVec::from_elem(None, &input_body.local_decls);
@@ -218,7 +222,7 @@ fn do_mir_borrowck<'tcx>(
218222
let mut body_owned = input_body.clone();
219223
let mut promoted = input_promoted.clone();
220224
let free_regions =
221-
nll::replace_regions_in_mir(infcx, param_env, &mut body_owned, &mut promoted);
225+
nll::replace_regions_in_mir(&infcx, param_env, &mut body_owned, &mut promoted);
222226
let body = &body_owned; // no further changes
223227

224228
let location_table_owned = LocationTable::new(body);
@@ -256,7 +260,7 @@ fn do_mir_borrowck<'tcx>(
256260
opt_closure_req,
257261
nll_errors,
258262
} = nll::compute_regions(
259-
infcx,
263+
&infcx,
260264
free_regions,
261265
body,
262266
&promoted,
@@ -271,12 +275,12 @@ fn do_mir_borrowck<'tcx>(
271275

272276
// Dump MIR results into a file, if that is enabled. This let us
273277
// write unit-tests, as well as helping with debugging.
274-
nll::dump_mir_results(infcx, &body, &regioncx, &opt_closure_req);
278+
nll::dump_mir_results(&infcx, &body, &regioncx, &opt_closure_req);
275279

276280
// We also have a `#[rustc_regions]` annotation that causes us to dump
277281
// information.
278282
nll::dump_annotation(
279-
infcx,
283+
&infcx,
280284
&body,
281285
&regioncx,
282286
&opt_closure_req,
@@ -320,7 +324,7 @@ fn do_mir_borrowck<'tcx>(
320324

321325
if let Err((move_data, move_errors)) = move_data_results {
322326
let mut promoted_mbcx = MirBorrowckCtxt {
323-
infcx,
327+
infcx: &infcx,
324328
param_env,
325329
body: promoted_body,
326330
move_data: &move_data,
@@ -349,7 +353,7 @@ fn do_mir_borrowck<'tcx>(
349353
}
350354

351355
let mut mbcx = MirBorrowckCtxt {
352-
infcx,
356+
infcx: &infcx,
353357
param_env,
354358
body,
355359
move_data: &mdpe.move_data,
@@ -481,8 +485,84 @@ pub struct BodyWithBorrowckFacts<'tcx> {
481485
pub location_table: LocationTable,
482486
}
483487

488+
pub struct BorrowckInferCtxt<'cx, 'tcx> {
489+
pub(crate) infcx: &'cx InferCtxt<'tcx>,
490+
pub(crate) reg_var_to_origin: RefCell<FxHashMap<ty::RegionVid, RegionCtxt>>,
491+
}
492+
493+
impl<'cx, 'tcx> BorrowckInferCtxt<'cx, 'tcx> {
494+
pub(crate) fn new(infcx: &'cx InferCtxt<'tcx>) -> Self {
495+
BorrowckInferCtxt { infcx, reg_var_to_origin: RefCell::new(Default::default()) }
496+
}
497+
498+
pub(crate) fn next_region_var<F>(
499+
&self,
500+
origin: RegionVariableOrigin,
501+
get_ctxt_fn: F,
502+
) -> ty::Region<'tcx>
503+
where
504+
F: Fn() -> RegionCtxt,
505+
{
506+
let next_region = self.infcx.next_region_var(origin);
507+
let vid = next_region
508+
.as_var()
509+
.unwrap_or_else(|| bug!("expected RegionKind::RegionVar on {:?}", next_region));
510+
511+
if cfg!(debug_assertions) {
512+
debug!("inserting vid {:?} with origin {:?} into var_to_origin", vid, origin);
513+
let ctxt = get_ctxt_fn();
514+
let mut var_to_origin = self.reg_var_to_origin.borrow_mut();
515+
let prev = var_to_origin.insert(vid, ctxt);
516+
517+
// This only makes sense if not called in a canonicalization context. If this
518+
// ever changes we either want to get rid of `BorrowckInferContext::reg_var_to_origin`
519+
// or modify how we track nll region vars for that map.
520+
assert!(matches!(prev, None));
521+
}
522+
523+
next_region
524+
}
525+
526+
#[instrument(skip(self, get_ctxt_fn), level = "debug")]
527+
pub(crate) fn next_nll_region_var<F>(
528+
&self,
529+
origin: NllRegionVariableOrigin,
530+
get_ctxt_fn: F,
531+
) -> ty::Region<'tcx>
532+
where
533+
F: Fn() -> RegionCtxt,
534+
{
535+
let next_region = self.infcx.next_nll_region_var(origin.clone());
536+
let vid = next_region
537+
.as_var()
538+
.unwrap_or_else(|| bug!("expected RegionKind::RegionVar on {:?}", next_region));
539+
540+
if cfg!(debug_assertions) {
541+
debug!("inserting vid {:?} with origin {:?} into var_to_origin", vid, origin);
542+
let ctxt = get_ctxt_fn();
543+
let mut var_to_origin = self.reg_var_to_origin.borrow_mut();
544+
let prev = var_to_origin.insert(vid, ctxt);
545+
546+
// This only makes sense if not called in a canonicalization context. If this
547+
// ever changes we either want to get rid of `BorrowckInferContext::reg_var_to_origin`
548+
// or modify how we track nll region vars for that map.
549+
assert!(matches!(prev, None));
550+
}
551+
552+
next_region
553+
}
554+
}
555+
556+
impl<'cx, 'tcx> Deref for BorrowckInferCtxt<'cx, 'tcx> {
557+
type Target = InferCtxt<'tcx>;
558+
559+
fn deref(&self) -> &'cx Self::Target {
560+
self.infcx
561+
}
562+
}
563+
484564
struct MirBorrowckCtxt<'cx, 'tcx> {
485-
infcx: &'cx InferCtxt<'tcx>,
565+
infcx: &'cx BorrowckInferCtxt<'cx, 'tcx>,
486566
param_env: ParamEnv<'tcx>,
487567
body: &'cx Body<'tcx>,
488568
move_data: &'cx MoveData<'tcx>,

compiler/rustc_borrowck/src/nll.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use rustc_data_structures::vec_map::VecMap;
66
use rustc_hir::def_id::LocalDefId;
77
use rustc_index::vec::IndexVec;
8-
use rustc_infer::infer::InferCtxt;
98
use rustc_middle::mir::{create_dump_file, dump_enabled, dump_mir, PassWhere};
109
use rustc_middle::mir::{
1110
BasicBlock, Body, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, Location,
@@ -37,7 +36,7 @@ use crate::{
3736
renumber,
3837
type_check::{self, MirTypeckRegionConstraints, MirTypeckResults},
3938
universal_regions::UniversalRegions,
40-
Upvar,
39+
BorrowckInferCtxt, Upvar,
4140
};
4241

4342
pub type PoloniusOutput = Output<RustcFacts>;
@@ -58,7 +57,7 @@ pub(crate) struct NllOutput<'tcx> {
5857
/// `compute_regions`.
5958
#[instrument(skip(infcx, param_env, body, promoted), level = "debug")]
6059
pub(crate) fn replace_regions_in_mir<'tcx>(
61-
infcx: &InferCtxt<'tcx>,
60+
infcx: &BorrowckInferCtxt<'_, 'tcx>,
6261
param_env: ty::ParamEnv<'tcx>,
6362
body: &mut Body<'tcx>,
6463
promoted: &mut IndexVec<Promoted, Body<'tcx>>,
@@ -157,7 +156,7 @@ fn populate_polonius_move_facts(
157156
///
158157
/// This may result in errors being reported.
159158
pub(crate) fn compute_regions<'cx, 'tcx>(
160-
infcx: &InferCtxt<'tcx>,
159+
infcx: &BorrowckInferCtxt<'_, 'tcx>,
161160
universal_regions: UniversalRegions<'tcx>,
162161
body: &Body<'tcx>,
163162
promoted: &IndexVec<Promoted, Body<'tcx>>,
@@ -259,6 +258,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
259258
);
260259

261260
let mut regioncx = RegionInferenceContext::new(
261+
infcx,
262262
var_origins,
263263
universal_regions,
264264
placeholder_indices,
@@ -322,7 +322,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
322322
}
323323

324324
pub(super) fn dump_mir_results<'tcx>(
325-
infcx: &InferCtxt<'tcx>,
325+
infcx: &BorrowckInferCtxt<'_, 'tcx>,
326326
body: &Body<'tcx>,
327327
regioncx: &RegionInferenceContext<'tcx>,
328328
closure_region_requirements: &Option<ClosureRegionRequirements<'_>>,
@@ -372,7 +372,7 @@ pub(super) fn dump_mir_results<'tcx>(
372372
#[allow(rustc::diagnostic_outside_of_impl)]
373373
#[allow(rustc::untranslatable_diagnostic)]
374374
pub(super) fn dump_annotation<'tcx>(
375-
infcx: &InferCtxt<'tcx>,
375+
infcx: &BorrowckInferCtxt<'_, 'tcx>,
376376
body: &Body<'tcx>,
377377
regioncx: &RegionInferenceContext<'tcx>,
378378
closure_region_requirements: &Option<ClosureRegionRequirements<'_>>,

compiler/rustc_borrowck/src/region_infer/mod.rs

+76-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use crate::{
3434
},
3535
type_check::{free_region_relations::UniversalRegionRelations, Locations},
3636
universal_regions::UniversalRegions,
37+
BorrowckInferCtxt,
3738
};
3839

3940
mod dump_mir;
@@ -243,6 +244,70 @@ pub enum ExtraConstraintInfo {
243244
PlaceholderFromPredicate(Span),
244245
}
245246

247+
#[instrument(skip(infcx, sccs), level = "debug")]
248+
fn sccs_info<'cx, 'tcx>(
249+
infcx: &'cx BorrowckInferCtxt<'cx, 'tcx>,
250+
sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>,
251+
) {
252+
use crate::renumber::RegionCtxt;
253+
254+
let var_to_origin = infcx.reg_var_to_origin.borrow();
255+
256+
let mut var_to_origin_sorted = var_to_origin.clone().into_iter().collect::<Vec<_>>();
257+
var_to_origin_sorted.sort_by(|a, b| a.0.cmp(&b.0));
258+
let mut debug_str = "region variables to origins:\n".to_string();
259+
for (reg_var, origin) in var_to_origin_sorted.into_iter() {
260+
debug_str.push_str(&format!("{:?}: {:?}\n", reg_var, origin));
261+
}
262+
debug!(debug_str);
263+
264+
let num_components = sccs.scc_data().ranges().len();
265+
let mut components = vec![FxHashSet::default(); num_components];
266+
267+
for (reg_var_idx, scc_idx) in sccs.scc_indices().iter().enumerate() {
268+
let reg_var = ty::RegionVid::from_usize(reg_var_idx);
269+
let origin = var_to_origin.get(&reg_var).unwrap_or_else(|| &RegionCtxt::Unknown);
270+
components[scc_idx.as_usize()].insert((reg_var, *origin));
271+
}
272+
273+
let mut components_str = "strongly connected components:".to_string();
274+
for (scc_idx, reg_vars_origins) in components.iter().enumerate() {
275+
let regions_info = reg_vars_origins.clone().into_iter().collect::<Vec<_>>();
276+
components_str.push_str(&format!(
277+
"{:?}: {:?})",
278+
ConstraintSccIndex::from_usize(scc_idx),
279+
regions_info,
280+
))
281+
}
282+
debug!(components_str);
283+
284+
// calculate the best representative for each component
285+
let components_representatives = components
286+
.into_iter()
287+
.enumerate()
288+
.map(|(scc_idx, region_ctxts)| {
289+
let repr = region_ctxts
290+
.into_iter()
291+
.map(|reg_var_origin| reg_var_origin.1)
292+
.max_by(|x, y| x.preference_value().cmp(&y.preference_value()))
293+
.unwrap();
294+
295+
(ConstraintSccIndex::from_usize(scc_idx), repr)
296+
})
297+
.collect::<FxHashMap<_, _>>();
298+
299+
let mut scc_node_to_edges = FxHashMap::default();
300+
for (scc_idx, repr) in components_representatives.iter() {
301+
let edges_range = sccs.scc_data().ranges()[*scc_idx].clone();
302+
let edges = &sccs.scc_data().all_successors()[edges_range];
303+
let edge_representatives =
304+
edges.iter().map(|scc_idx| components_representatives[scc_idx]).collect::<Vec<_>>();
305+
scc_node_to_edges.insert((scc_idx, repr), edge_representatives);
306+
}
307+
308+
debug!("SCC edges {:#?}", scc_node_to_edges);
309+
}
310+
246311
impl<'tcx> RegionInferenceContext<'tcx> {
247312
/// Creates a new region inference context with a total of
248313
/// `num_region_variables` valid inference variables; the first N
@@ -251,7 +316,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
251316
///
252317
/// The `outlives_constraints` and `type_tests` are an initial set
253318
/// of constraints produced by the MIR type check.
254-
pub(crate) fn new(
319+
pub(crate) fn new<'cx>(
320+
_infcx: &BorrowckInferCtxt<'cx, 'tcx>,
255321
var_infos: VarInfos,
256322
universal_regions: Rc<UniversalRegions<'tcx>>,
257323
placeholder_indices: Rc<PlaceholderIndices>,
@@ -263,6 +329,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
263329
liveness_constraints: LivenessValues<RegionVid>,
264330
elements: &Rc<RegionValueElements>,
265331
) -> Self {
332+
debug!("universal_regions: {:#?}", universal_regions);
333+
debug!("outlives constraints: {:#?}", outlives_constraints);
334+
debug!("placeholder_indices: {:#?}", placeholder_indices);
335+
debug!("type tests: {:#?}", type_tests);
336+
266337
// Create a RegionDefinition for each inference variable.
267338
let definitions: IndexVec<_, _> = var_infos
268339
.iter()
@@ -274,6 +345,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
274345
let fr_static = universal_regions.fr_static;
275346
let constraint_sccs = Rc::new(constraints.compute_sccs(&constraint_graph, fr_static));
276347

348+
if cfg!(debug_assertions) {
349+
sccs_info(_infcx, constraint_sccs.clone());
350+
}
351+
277352
let mut scc_values =
278353
RegionValues::new(elements, universal_regions.len(), &placeholder_indices);
279354

compiler/rustc_borrowck/src/region_infer/values.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ impl<N: Idx> LivenessValues<N> {
181181
/// Maps from `ty::PlaceholderRegion` values that are used in the rest of
182182
/// rustc to the internal `PlaceholderIndex` values that are used in
183183
/// NLL.
184-
#[derive(Default)]
184+
#[derive(Debug, Default)]
185185
pub(crate) struct PlaceholderIndices {
186186
indices: FxIndexSet<ty::PlaceholderRegion>,
187187
}

0 commit comments

Comments
 (0)