Skip to content

Commit a0245bb

Browse files
committed
rework the leak_check to take the outer_universe
clean up coherence to not rely on probes anymore
1 parent 2a4467d commit a0245bb

File tree

10 files changed

+178
-166
lines changed

10 files changed

+178
-166
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
808808
G: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
809809
{
810810
self.commit_if_ok(|snapshot| {
811+
let outer_universe = self.infcx.universe();
812+
811813
let result = if let ty::FnPtr(fn_ty_b) = b.kind()
812814
&& let (hir::Unsafety::Normal, hir::Unsafety::Unsafe) =
813815
(fn_ty_a.unsafety(), fn_ty_b.unsafety())
@@ -824,7 +826,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
824826
// want the coerced type to be the actual supertype of these two,
825827
// but for now, we want to just error to ensure we don't lock
826828
// ourselves into a specific behavior with NLL.
827-
self.leak_check(snapshot)?;
829+
self.leak_check(outer_universe, Some(snapshot))?;
828830

829831
result
830832
})

compiler/rustc_infer/src/infer/at.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ impl<'tcx> InferCtxt<'tcx> {
7070
tcx: self.tcx,
7171
defining_use_anchor: self.defining_use_anchor,
7272
considering_regions: self.considering_regions,
73+
skip_leak_check: self.skip_leak_check,
7374
inner: self.inner.clone(),
74-
skip_leak_check: self.skip_leak_check.clone(),
7575
lexical_region_resolutions: self.lexical_region_resolutions.clone(),
7676
selection_cache: self.selection_cache.clone(),
7777
evaluation_cache: self.evaluation_cache.clone(),

compiler/rustc_infer/src/infer/higher_ranked/mod.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,24 +105,31 @@ impl<'tcx> InferCtxt<'tcx> {
105105
self.tcx.replace_bound_vars_uncached(binder, delegate)
106106
}
107107

108-
/// See [RegionConstraintCollector::leak_check][1].
108+
/// See [RegionConstraintCollector::leak_check][1]. We only check placeholder
109+
/// leaking into `outer_universe`, i.e. placeholders which cannot be named by that
110+
/// universe.
109111
///
110112
/// [1]: crate::infer::region_constraints::RegionConstraintCollector::leak_check
111-
pub fn leak_check(&self, snapshot: &CombinedSnapshot<'tcx>) -> RelateResult<'tcx, ()> {
113+
pub fn leak_check(
114+
&self,
115+
outer_universe: ty::UniverseIndex,
116+
only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
117+
) -> RelateResult<'tcx, ()> {
112118
// If the user gave `-Zno-leak-check`, or we have been
113119
// configured to skip the leak check, then skip the leak check
114120
// completely. The leak check is deprecated. Any legitimate
115121
// subtyping errors that it would have caught will now be
116122
// caught later on, during region checking. However, we
117123
// continue to use it for a transition period.
118-
if self.tcx.sess.opts.unstable_opts.no_leak_check || self.skip_leak_check.get() {
124+
if self.tcx.sess.opts.unstable_opts.no_leak_check || self.skip_leak_check {
119125
return Ok(());
120126
}
121127

122128
self.inner.borrow_mut().unwrap_region_constraints().leak_check(
123129
self.tcx,
130+
outer_universe,
124131
self.universe(),
125-
snapshot,
132+
only_consider_snapshot,
126133
)
127134
}
128135
}

compiler/rustc_infer/src/infer/mod.rs

Lines changed: 22 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -251,14 +251,13 @@ pub struct InferCtxt<'tcx> {
251251
/// solving is left to borrowck instead.
252252
pub considering_regions: bool,
253253

254-
pub inner: RefCell<InferCtxtInner<'tcx>>,
255-
256254
/// If set, this flag causes us to skip the 'leak check' during
257255
/// higher-ranked subtyping operations. This flag is a temporary one used
258256
/// to manage the removal of the leak-check: for the time being, we still run the
259-
/// leak-check, but we issue warnings. This flag can only be set to true
260-
/// when entering a snapshot.
261-
skip_leak_check: Cell<bool>,
257+
/// leak-check, but we issue warnings.
258+
skip_leak_check: bool,
259+
260+
pub inner: RefCell<InferCtxtInner<'tcx>>,
262261

263262
/// Once region inference is done, the values for each variable.
264263
lexical_region_resolutions: RefCell<Option<LexicalRegionResolutions<'tcx>>>,
@@ -543,6 +542,7 @@ pub struct InferCtxtBuilder<'tcx> {
543542
tcx: TyCtxt<'tcx>,
544543
defining_use_anchor: DefiningAnchor,
545544
considering_regions: bool,
545+
skip_leak_check: bool,
546546
/// Whether we are in coherence mode.
547547
intercrate: bool,
548548
}
@@ -557,6 +557,7 @@ impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> {
557557
tcx: self,
558558
defining_use_anchor: DefiningAnchor::Error,
559559
considering_regions: true,
560+
skip_leak_check: false,
560561
intercrate: false,
561562
}
562563
}
@@ -584,6 +585,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
584585
self
585586
}
586587

588+
pub fn skip_leak_check(mut self, skip_leak_check: bool) -> Self {
589+
self.skip_leak_check = skip_leak_check;
590+
self
591+
}
592+
587593
/// Given a canonical value `C` as a starting point, create an
588594
/// inference context that contains each of the bound values
589595
/// within instantiated as a fresh variable. The `f` closure is
@@ -605,11 +611,18 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
605611
}
606612

607613
pub fn build(&mut self) -> InferCtxt<'tcx> {
608-
let InferCtxtBuilder { tcx, defining_use_anchor, considering_regions, intercrate } = *self;
614+
let InferCtxtBuilder {
615+
tcx,
616+
defining_use_anchor,
617+
considering_regions,
618+
skip_leak_check,
619+
intercrate,
620+
} = *self;
609621
InferCtxt {
610622
tcx,
611623
defining_use_anchor,
612624
considering_regions,
625+
skip_leak_check,
613626
inner: RefCell::new(InferCtxtInner::new()),
614627
lexical_region_resolutions: RefCell::new(None),
615628
selection_cache: Default::default(),
@@ -619,7 +632,6 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
619632
tainted_by_errors: Cell::new(None),
620633
err_count_on_creation: tcx.sess.err_count(),
621634
in_snapshot: Cell::new(false),
622-
skip_leak_check: Cell::new(false),
623635
universe: Cell::new(ty::UniverseIndex::ROOT),
624636
intercrate,
625637
}
@@ -815,32 +827,9 @@ impl<'tcx> InferCtxt<'tcx> {
815827
r
816828
}
817829

818-
/// If `should_skip` is true, then execute `f` then unroll any bindings it creates.
819-
#[instrument(skip(self, f), level = "debug")]
820-
pub fn probe_maybe_skip_leak_check<R, F>(&self, should_skip: bool, f: F) -> R
821-
where
822-
F: FnOnce(&CombinedSnapshot<'tcx>) -> R,
823-
{
824-
let snapshot = self.start_snapshot();
825-
let was_skip_leak_check = self.skip_leak_check.get();
826-
if should_skip {
827-
self.skip_leak_check.set(true);
828-
}
829-
let r = f(&snapshot);
830-
self.rollback_to("probe", snapshot);
831-
self.skip_leak_check.set(was_skip_leak_check);
832-
r
833-
}
834-
835-
/// Scan the constraints produced since `snapshot` began and returns:
836-
///
837-
/// - `None` -- if none of them involves "region outlives" constraints.
838-
/// - `Some(true)` -- if there are `'a: 'b` constraints where `'a` or `'b` is a placeholder.
839-
/// - `Some(false)` -- if there are `'a: 'b` constraints but none involve placeholders.
840-
pub fn region_constraints_added_in_snapshot(
841-
&self,
842-
snapshot: &CombinedSnapshot<'tcx>,
843-
) -> Option<bool> {
830+
/// Scan the constraints produced since `snapshot` and check whether
831+
/// we added any region constraints.
832+
pub fn region_constraints_added_in_snapshot(&self, snapshot: &CombinedSnapshot<'tcx>) -> bool {
844833
self.inner
845834
.borrow_mut()
846835
.unwrap_region_constraints()

compiler/rustc_infer/src/infer/region_constraints/leak_check.rs

Lines changed: 69 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::infer::CombinedSnapshot;
33
use rustc_data_structures::{
44
fx::FxIndexMap,
55
graph::{scc::Sccs, vec_graph::VecGraph},
6-
undo_log::UndoLogs,
76
};
87
use rustc_index::Idx;
98
use rustc_middle::ty::error::TypeError;
@@ -13,7 +12,9 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
1312
/// Searches new universes created during `snapshot`, looking for
1413
/// placeholders that may "leak" out from the universes they are contained
1514
/// in. If any leaking placeholders are found, then an `Err` is returned
16-
/// (typically leading to the snapshot being reversed).
15+
/// (typically leading to the snapshot being reversed). This algorithm
16+
/// only looks at placeholders which cannot be named by `outer_universe`,
17+
/// as this is the universe we're currently checking for a leak.
1718
///
1819
/// The leak check *used* to be the only way we had to handle higher-ranked
1920
/// obligations. Now that we have integrated universes into the region
@@ -55,36 +56,34 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
5556
/// * if they must also be equal to a placeholder P, and U cannot name P, report an error, as that
5657
/// indicates `P: R` and `R` is in an incompatible universe
5758
///
59+
/// To improve performance and for the old trait solver caching to be sound, this takes
60+
/// an optional snapshot in which case we only look at region constraints added in that
61+
/// snapshot. If we were to not do that the `leak_check` during evaluation can rely on
62+
/// region constraints added outside of that evaluation. As that is not reflected in the
63+
/// cache key this would be unsound.
64+
///
5865
/// # Historical note
5966
///
6067
/// Older variants of the leak check used to report errors for these
6168
/// patterns, but we no longer do:
6269
///
6370
/// * R: P1, even if R cannot name P1, because R = 'static is a valid sol'n
6471
/// * R: P1, R: P2, as above
72+
#[instrument(level = "debug", skip(self, tcx, only_consider_snapshot), ret)]
6573
pub fn leak_check(
6674
&mut self,
6775
tcx: TyCtxt<'tcx>,
76+
outer_universe: ty::UniverseIndex,
6877
max_universe: ty::UniverseIndex,
69-
snapshot: &CombinedSnapshot<'tcx>,
78+
only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
7079
) -> RelateResult<'tcx, ()> {
71-
debug!(
72-
"leak_check(max_universe={:?}, snapshot.universe={:?})",
73-
max_universe, snapshot.universe
74-
);
75-
76-
assert!(UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log));
77-
78-
let universe_at_start_of_snapshot = snapshot.universe;
79-
if universe_at_start_of_snapshot == max_universe {
80+
if outer_universe == max_universe {
8081
return Ok(());
8182
}
8283

83-
let mini_graph =
84-
&MiniGraph::new(tcx, self.undo_log.region_constraints(), &self.storage.data.verifys);
84+
let mini_graph = &MiniGraph::new(tcx, &self, only_consider_snapshot);
8585

86-
let mut leak_check =
87-
LeakCheck::new(tcx, universe_at_start_of_snapshot, max_universe, mini_graph, self);
86+
let mut leak_check = LeakCheck::new(tcx, outer_universe, max_universe, mini_graph, self);
8887
leak_check.assign_placeholder_values()?;
8988
leak_check.propagate_scc_value()?;
9089
Ok(())
@@ -93,7 +92,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
9392

9493
struct LeakCheck<'me, 'tcx> {
9594
tcx: TyCtxt<'tcx>,
96-
universe_at_start_of_snapshot: ty::UniverseIndex,
95+
outer_universe: ty::UniverseIndex,
9796
mini_graph: &'me MiniGraph<'tcx>,
9897
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
9998

@@ -121,15 +120,15 @@ struct LeakCheck<'me, 'tcx> {
121120
impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
122121
fn new(
123122
tcx: TyCtxt<'tcx>,
124-
universe_at_start_of_snapshot: ty::UniverseIndex,
123+
outer_universe: ty::UniverseIndex,
125124
max_universe: ty::UniverseIndex,
126125
mini_graph: &'me MiniGraph<'tcx>,
127126
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
128127
) -> Self {
129128
let dummy_scc_universe = SccUniverse { universe: max_universe, region: None };
130129
Self {
131130
tcx,
132-
universe_at_start_of_snapshot,
131+
outer_universe,
133132
mini_graph,
134133
rcc,
135134
scc_placeholders: IndexVec::from_elem_n(None, mini_graph.sccs.num_sccs()),
@@ -154,7 +153,7 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
154153

155154
// Detect those SCCs that directly contain a placeholder
156155
if let ty::RePlaceholder(placeholder) = **region {
157-
if self.universe_at_start_of_snapshot.cannot_name(placeholder.universe) {
156+
if self.outer_universe.cannot_name(placeholder.universe) {
158157
self.assign_scc_value(scc, placeholder)?;
159158
}
160159
}
@@ -364,56 +363,70 @@ struct MiniGraph<'tcx> {
364363
}
365364

366365
impl<'tcx> MiniGraph<'tcx> {
367-
fn new<'a>(
366+
fn new(
368367
tcx: TyCtxt<'tcx>,
369-
undo_log: impl Iterator<Item = &'a UndoLog<'tcx>>,
370-
verifys: &[Verify<'tcx>],
371-
) -> Self
372-
where
373-
'tcx: 'a,
374-
{
368+
region_constraints: &RegionConstraintCollector<'_, 'tcx>,
369+
only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
370+
) -> Self {
375371
let mut nodes = FxIndexMap::default();
376372
let mut edges = Vec::new();
377373

378374
// Note that if `R2: R1`, we get a callback `r1, r2`, so `target` is first parameter.
379-
Self::iterate_undo_log(tcx, undo_log, verifys, |target, source| {
380-
let source_node = Self::add_node(&mut nodes, source);
381-
let target_node = Self::add_node(&mut nodes, target);
382-
edges.push((source_node, target_node));
383-
});
375+
Self::iterate_region_constraints(
376+
tcx,
377+
region_constraints,
378+
only_consider_snapshot,
379+
|target, source| {
380+
let source_node = Self::add_node(&mut nodes, source);
381+
let target_node = Self::add_node(&mut nodes, target);
382+
edges.push((source_node, target_node));
383+
},
384+
);
384385
let graph = VecGraph::new(nodes.len(), edges);
385386
let sccs = Sccs::new(&graph);
386387
Self { nodes, sccs }
387388
}
388389

389390
/// Invokes `each_edge(R1, R2)` for each edge where `R2: R1`
390-
fn iterate_undo_log<'a>(
391+
fn iterate_region_constraints(
391392
tcx: TyCtxt<'tcx>,
392-
undo_log: impl Iterator<Item = &'a UndoLog<'tcx>>,
393-
verifys: &[Verify<'tcx>],
393+
region_constraints: &RegionConstraintCollector<'_, 'tcx>,
394+
only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
394395
mut each_edge: impl FnMut(ty::Region<'tcx>, ty::Region<'tcx>),
395-
) where
396-
'tcx: 'a,
397-
{
398-
for undo_entry in undo_log {
399-
match undo_entry {
400-
&AddConstraint(Constraint::VarSubVar(a, b)) => {
401-
each_edge(ty::Region::new_var(tcx, a), ty::Region::new_var(tcx, b));
402-
}
403-
&AddConstraint(Constraint::RegSubVar(a, b)) => {
404-
each_edge(a, ty::Region::new_var(tcx, b));
405-
}
406-
&AddConstraint(Constraint::VarSubReg(a, b)) => {
407-
each_edge(ty::Region::new_var(tcx, a), b);
408-
}
409-
&AddConstraint(Constraint::RegSubReg(a, b)) => {
410-
each_edge(a, b);
396+
) {
397+
let mut each_constraint = |constraint| match constraint {
398+
&Constraint::VarSubVar(a, b) => {
399+
each_edge(ty::Region::new_var(tcx, a), ty::Region::new_var(tcx, b));
400+
}
401+
&Constraint::RegSubVar(a, b) => {
402+
each_edge(a, ty::Region::new_var(tcx, b));
403+
}
404+
&Constraint::VarSubReg(a, b) => {
405+
each_edge(ty::Region::new_var(tcx, a), b);
406+
}
407+
&Constraint::RegSubReg(a, b) => {
408+
each_edge(a, b);
409+
}
410+
};
411+
412+
if let Some(snapshot) = only_consider_snapshot {
413+
for undo_entry in
414+
region_constraints.undo_log.region_constraints_in_snapshot(&snapshot.undo_snapshot)
415+
{
416+
match undo_entry {
417+
AddConstraint(constraint) => {
418+
each_constraint(constraint);
419+
}
420+
&AddVerify(i) => span_bug!(
421+
region_constraints.data().verifys[i].origin.span(),
422+
"we never add verifications while doing higher-ranked things",
423+
),
424+
&AddCombination(..) | &AddVar(..) => {}
411425
}
412-
&AddVerify(i) => span_bug!(
413-
verifys[i].origin.span(),
414-
"we never add verifications while doing higher-ranked things",
415-
),
416-
&AddCombination(..) | &AddVar(..) => {}
426+
}
427+
} else {
428+
for (constraint, _origin) in &region_constraints.data().constraints {
429+
each_constraint(constraint)
417430
}
418431
}
419432
}

0 commit comments

Comments
 (0)