rework the leak_check to take the outer_universe
clean up coherence to not rely on probes anymore
This commit is contained in:
parent
2a4467da9f
commit
a0245bb3cb
10 changed files with 178 additions and 166 deletions
|
@ -808,6 +808,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
||||||
G: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
|
G: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
|
||||||
{
|
{
|
||||||
self.commit_if_ok(|snapshot| {
|
self.commit_if_ok(|snapshot| {
|
||||||
|
let outer_universe = self.infcx.universe();
|
||||||
|
|
||||||
let result = if let ty::FnPtr(fn_ty_b) = b.kind()
|
let result = if let ty::FnPtr(fn_ty_b) = b.kind()
|
||||||
&& let (hir::Unsafety::Normal, hir::Unsafety::Unsafe) =
|
&& let (hir::Unsafety::Normal, hir::Unsafety::Unsafe) =
|
||||||
(fn_ty_a.unsafety(), fn_ty_b.unsafety())
|
(fn_ty_a.unsafety(), fn_ty_b.unsafety())
|
||||||
|
@ -824,7 +826,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
||||||
// want the coerced type to be the actual supertype of these two,
|
// want the coerced type to be the actual supertype of these two,
|
||||||
// but for now, we want to just error to ensure we don't lock
|
// but for now, we want to just error to ensure we don't lock
|
||||||
// ourselves into a specific behavior with NLL.
|
// ourselves into a specific behavior with NLL.
|
||||||
self.leak_check(snapshot)?;
|
self.leak_check(outer_universe, Some(snapshot))?;
|
||||||
|
|
||||||
result
|
result
|
||||||
})
|
})
|
||||||
|
|
|
@ -70,8 +70,8 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||||
tcx: self.tcx,
|
tcx: self.tcx,
|
||||||
defining_use_anchor: self.defining_use_anchor,
|
defining_use_anchor: self.defining_use_anchor,
|
||||||
considering_regions: self.considering_regions,
|
considering_regions: self.considering_regions,
|
||||||
|
skip_leak_check: self.skip_leak_check,
|
||||||
inner: self.inner.clone(),
|
inner: self.inner.clone(),
|
||||||
skip_leak_check: self.skip_leak_check.clone(),
|
|
||||||
lexical_region_resolutions: self.lexical_region_resolutions.clone(),
|
lexical_region_resolutions: self.lexical_region_resolutions.clone(),
|
||||||
selection_cache: self.selection_cache.clone(),
|
selection_cache: self.selection_cache.clone(),
|
||||||
evaluation_cache: self.evaluation_cache.clone(),
|
evaluation_cache: self.evaluation_cache.clone(),
|
||||||
|
|
|
@ -105,24 +105,31 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||||
self.tcx.replace_bound_vars_uncached(binder, delegate)
|
self.tcx.replace_bound_vars_uncached(binder, delegate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See [RegionConstraintCollector::leak_check][1].
|
/// See [RegionConstraintCollector::leak_check][1]. We only check placeholder
|
||||||
|
/// leaking into `outer_universe`, i.e. placeholders which cannot be named by that
|
||||||
|
/// universe.
|
||||||
///
|
///
|
||||||
/// [1]: crate::infer::region_constraints::RegionConstraintCollector::leak_check
|
/// [1]: crate::infer::region_constraints::RegionConstraintCollector::leak_check
|
||||||
pub fn leak_check(&self, snapshot: &CombinedSnapshot<'tcx>) -> RelateResult<'tcx, ()> {
|
pub fn leak_check(
|
||||||
|
&self,
|
||||||
|
outer_universe: ty::UniverseIndex,
|
||||||
|
only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
|
||||||
|
) -> RelateResult<'tcx, ()> {
|
||||||
// If the user gave `-Zno-leak-check`, or we have been
|
// If the user gave `-Zno-leak-check`, or we have been
|
||||||
// configured to skip the leak check, then skip the leak check
|
// configured to skip the leak check, then skip the leak check
|
||||||
// completely. The leak check is deprecated. Any legitimate
|
// completely. The leak check is deprecated. Any legitimate
|
||||||
// subtyping errors that it would have caught will now be
|
// subtyping errors that it would have caught will now be
|
||||||
// caught later on, during region checking. However, we
|
// caught later on, during region checking. However, we
|
||||||
// continue to use it for a transition period.
|
// continue to use it for a transition period.
|
||||||
if self.tcx.sess.opts.unstable_opts.no_leak_check || self.skip_leak_check.get() {
|
if self.tcx.sess.opts.unstable_opts.no_leak_check || self.skip_leak_check {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inner.borrow_mut().unwrap_region_constraints().leak_check(
|
self.inner.borrow_mut().unwrap_region_constraints().leak_check(
|
||||||
self.tcx,
|
self.tcx,
|
||||||
|
outer_universe,
|
||||||
self.universe(),
|
self.universe(),
|
||||||
snapshot,
|
only_consider_snapshot,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,14 +251,13 @@ pub struct InferCtxt<'tcx> {
|
||||||
/// solving is left to borrowck instead.
|
/// solving is left to borrowck instead.
|
||||||
pub considering_regions: bool,
|
pub considering_regions: bool,
|
||||||
|
|
||||||
pub inner: RefCell<InferCtxtInner<'tcx>>,
|
|
||||||
|
|
||||||
/// If set, this flag causes us to skip the 'leak check' during
|
/// If set, this flag causes us to skip the 'leak check' during
|
||||||
/// higher-ranked subtyping operations. This flag is a temporary one used
|
/// higher-ranked subtyping operations. This flag is a temporary one used
|
||||||
/// to manage the removal of the leak-check: for the time being, we still run the
|
/// to manage the removal of the leak-check: for the time being, we still run the
|
||||||
/// leak-check, but we issue warnings. This flag can only be set to true
|
/// leak-check, but we issue warnings.
|
||||||
/// when entering a snapshot.
|
skip_leak_check: bool,
|
||||||
skip_leak_check: Cell<bool>,
|
|
||||||
|
pub inner: RefCell<InferCtxtInner<'tcx>>,
|
||||||
|
|
||||||
/// Once region inference is done, the values for each variable.
|
/// Once region inference is done, the values for each variable.
|
||||||
lexical_region_resolutions: RefCell<Option<LexicalRegionResolutions<'tcx>>>,
|
lexical_region_resolutions: RefCell<Option<LexicalRegionResolutions<'tcx>>>,
|
||||||
|
@ -543,6 +542,7 @@ pub struct InferCtxtBuilder<'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
defining_use_anchor: DefiningAnchor,
|
defining_use_anchor: DefiningAnchor,
|
||||||
considering_regions: bool,
|
considering_regions: bool,
|
||||||
|
skip_leak_check: bool,
|
||||||
/// Whether we are in coherence mode.
|
/// Whether we are in coherence mode.
|
||||||
intercrate: bool,
|
intercrate: bool,
|
||||||
}
|
}
|
||||||
|
@ -557,6 +557,7 @@ impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> {
|
||||||
tcx: self,
|
tcx: self,
|
||||||
defining_use_anchor: DefiningAnchor::Error,
|
defining_use_anchor: DefiningAnchor::Error,
|
||||||
considering_regions: true,
|
considering_regions: true,
|
||||||
|
skip_leak_check: false,
|
||||||
intercrate: false,
|
intercrate: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -584,6 +585,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn skip_leak_check(mut self, skip_leak_check: bool) -> Self {
|
||||||
|
self.skip_leak_check = skip_leak_check;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Given a canonical value `C` as a starting point, create an
|
/// Given a canonical value `C` as a starting point, create an
|
||||||
/// inference context that contains each of the bound values
|
/// inference context that contains each of the bound values
|
||||||
/// within instantiated as a fresh variable. The `f` closure is
|
/// within instantiated as a fresh variable. The `f` closure is
|
||||||
|
@ -605,11 +611,18 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(&mut self) -> InferCtxt<'tcx> {
|
pub fn build(&mut self) -> InferCtxt<'tcx> {
|
||||||
let InferCtxtBuilder { tcx, defining_use_anchor, considering_regions, intercrate } = *self;
|
let InferCtxtBuilder {
|
||||||
|
tcx,
|
||||||
|
defining_use_anchor,
|
||||||
|
considering_regions,
|
||||||
|
skip_leak_check,
|
||||||
|
intercrate,
|
||||||
|
} = *self;
|
||||||
InferCtxt {
|
InferCtxt {
|
||||||
tcx,
|
tcx,
|
||||||
defining_use_anchor,
|
defining_use_anchor,
|
||||||
considering_regions,
|
considering_regions,
|
||||||
|
skip_leak_check,
|
||||||
inner: RefCell::new(InferCtxtInner::new()),
|
inner: RefCell::new(InferCtxtInner::new()),
|
||||||
lexical_region_resolutions: RefCell::new(None),
|
lexical_region_resolutions: RefCell::new(None),
|
||||||
selection_cache: Default::default(),
|
selection_cache: Default::default(),
|
||||||
|
@ -619,7 +632,6 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
||||||
tainted_by_errors: Cell::new(None),
|
tainted_by_errors: Cell::new(None),
|
||||||
err_count_on_creation: tcx.sess.err_count(),
|
err_count_on_creation: tcx.sess.err_count(),
|
||||||
in_snapshot: Cell::new(false),
|
in_snapshot: Cell::new(false),
|
||||||
skip_leak_check: Cell::new(false),
|
|
||||||
universe: Cell::new(ty::UniverseIndex::ROOT),
|
universe: Cell::new(ty::UniverseIndex::ROOT),
|
||||||
intercrate,
|
intercrate,
|
||||||
}
|
}
|
||||||
|
@ -815,32 +827,9 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If `should_skip` is true, then execute `f` then unroll any bindings it creates.
|
/// Scan the constraints produced since `snapshot` and check whether
|
||||||
#[instrument(skip(self, f), level = "debug")]
|
/// we added any region constraints.
|
||||||
pub fn probe_maybe_skip_leak_check<R, F>(&self, should_skip: bool, f: F) -> R
|
pub fn region_constraints_added_in_snapshot(&self, snapshot: &CombinedSnapshot<'tcx>) -> bool {
|
||||||
where
|
|
||||||
F: FnOnce(&CombinedSnapshot<'tcx>) -> R,
|
|
||||||
{
|
|
||||||
let snapshot = self.start_snapshot();
|
|
||||||
let was_skip_leak_check = self.skip_leak_check.get();
|
|
||||||
if should_skip {
|
|
||||||
self.skip_leak_check.set(true);
|
|
||||||
}
|
|
||||||
let r = f(&snapshot);
|
|
||||||
self.rollback_to("probe", snapshot);
|
|
||||||
self.skip_leak_check.set(was_skip_leak_check);
|
|
||||||
r
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Scan the constraints produced since `snapshot` began and returns:
|
|
||||||
///
|
|
||||||
/// - `None` -- if none of them involves "region outlives" constraints.
|
|
||||||
/// - `Some(true)` -- if there are `'a: 'b` constraints where `'a` or `'b` is a placeholder.
|
|
||||||
/// - `Some(false)` -- if there are `'a: 'b` constraints but none involve placeholders.
|
|
||||||
pub fn region_constraints_added_in_snapshot(
|
|
||||||
&self,
|
|
||||||
snapshot: &CombinedSnapshot<'tcx>,
|
|
||||||
) -> Option<bool> {
|
|
||||||
self.inner
|
self.inner
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.unwrap_region_constraints()
|
.unwrap_region_constraints()
|
||||||
|
|
|
@ -3,7 +3,6 @@ use crate::infer::CombinedSnapshot;
|
||||||
use rustc_data_structures::{
|
use rustc_data_structures::{
|
||||||
fx::FxIndexMap,
|
fx::FxIndexMap,
|
||||||
graph::{scc::Sccs, vec_graph::VecGraph},
|
graph::{scc::Sccs, vec_graph::VecGraph},
|
||||||
undo_log::UndoLogs,
|
|
||||||
};
|
};
|
||||||
use rustc_index::Idx;
|
use rustc_index::Idx;
|
||||||
use rustc_middle::ty::error::TypeError;
|
use rustc_middle::ty::error::TypeError;
|
||||||
|
@ -13,7 +12,9 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
||||||
/// Searches new universes created during `snapshot`, looking for
|
/// Searches new universes created during `snapshot`, looking for
|
||||||
/// placeholders that may "leak" out from the universes they are contained
|
/// placeholders that may "leak" out from the universes they are contained
|
||||||
/// in. If any leaking placeholders are found, then an `Err` is returned
|
/// in. If any leaking placeholders are found, then an `Err` is returned
|
||||||
/// (typically leading to the snapshot being reversed).
|
/// (typically leading to the snapshot being reversed). This algorithm
|
||||||
|
/// only looks at placeholders which cannot be named by `outer_universe`,
|
||||||
|
/// as this is the universe we're currently checking for a leak.
|
||||||
///
|
///
|
||||||
/// The leak check *used* to be the only way we had to handle higher-ranked
|
/// The leak check *used* to be the only way we had to handle higher-ranked
|
||||||
/// obligations. Now that we have integrated universes into the region
|
/// obligations. Now that we have integrated universes into the region
|
||||||
|
@ -55,6 +56,12 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
||||||
/// * if they must also be equal to a placeholder P, and U cannot name P, report an error, as that
|
/// * if they must also be equal to a placeholder P, and U cannot name P, report an error, as that
|
||||||
/// indicates `P: R` and `R` is in an incompatible universe
|
/// indicates `P: R` and `R` is in an incompatible universe
|
||||||
///
|
///
|
||||||
|
/// To improve performance and for the old trait solver caching to be sound, this takes
|
||||||
|
/// an optional snapshot in which case we only look at region constraints added in that
|
||||||
|
/// snapshot. If we were to not do that the `leak_check` during evaluation can rely on
|
||||||
|
/// region constraints added outside of that evaluation. As that is not reflected in the
|
||||||
|
/// cache key this would be unsound.
|
||||||
|
///
|
||||||
/// # Historical note
|
/// # Historical note
|
||||||
///
|
///
|
||||||
/// Older variants of the leak check used to report errors for these
|
/// Older variants of the leak check used to report errors for these
|
||||||
|
@ -62,29 +69,21 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
||||||
///
|
///
|
||||||
/// * R: P1, even if R cannot name P1, because R = 'static is a valid sol'n
|
/// * R: P1, even if R cannot name P1, because R = 'static is a valid sol'n
|
||||||
/// * R: P1, R: P2, as above
|
/// * R: P1, R: P2, as above
|
||||||
|
#[instrument(level = "debug", skip(self, tcx, only_consider_snapshot), ret)]
|
||||||
pub fn leak_check(
|
pub fn leak_check(
|
||||||
&mut self,
|
&mut self,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
|
outer_universe: ty::UniverseIndex,
|
||||||
max_universe: ty::UniverseIndex,
|
max_universe: ty::UniverseIndex,
|
||||||
snapshot: &CombinedSnapshot<'tcx>,
|
only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
|
||||||
) -> RelateResult<'tcx, ()> {
|
) -> RelateResult<'tcx, ()> {
|
||||||
debug!(
|
if outer_universe == max_universe {
|
||||||
"leak_check(max_universe={:?}, snapshot.universe={:?})",
|
|
||||||
max_universe, snapshot.universe
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log));
|
|
||||||
|
|
||||||
let universe_at_start_of_snapshot = snapshot.universe;
|
|
||||||
if universe_at_start_of_snapshot == max_universe {
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mini_graph =
|
let mini_graph = &MiniGraph::new(tcx, &self, only_consider_snapshot);
|
||||||
&MiniGraph::new(tcx, self.undo_log.region_constraints(), &self.storage.data.verifys);
|
|
||||||
|
|
||||||
let mut leak_check =
|
let mut leak_check = LeakCheck::new(tcx, outer_universe, max_universe, mini_graph, self);
|
||||||
LeakCheck::new(tcx, universe_at_start_of_snapshot, max_universe, mini_graph, self);
|
|
||||||
leak_check.assign_placeholder_values()?;
|
leak_check.assign_placeholder_values()?;
|
||||||
leak_check.propagate_scc_value()?;
|
leak_check.propagate_scc_value()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -93,7 +92,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
||||||
|
|
||||||
struct LeakCheck<'me, 'tcx> {
|
struct LeakCheck<'me, 'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
universe_at_start_of_snapshot: ty::UniverseIndex,
|
outer_universe: ty::UniverseIndex,
|
||||||
mini_graph: &'me MiniGraph<'tcx>,
|
mini_graph: &'me MiniGraph<'tcx>,
|
||||||
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
|
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
|
||||||
|
|
||||||
|
@ -121,7 +120,7 @@ struct LeakCheck<'me, 'tcx> {
|
||||||
impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
|
impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
|
||||||
fn new(
|
fn new(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
universe_at_start_of_snapshot: ty::UniverseIndex,
|
outer_universe: ty::UniverseIndex,
|
||||||
max_universe: ty::UniverseIndex,
|
max_universe: ty::UniverseIndex,
|
||||||
mini_graph: &'me MiniGraph<'tcx>,
|
mini_graph: &'me MiniGraph<'tcx>,
|
||||||
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
|
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
|
||||||
|
@ -129,7 +128,7 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
|
||||||
let dummy_scc_universe = SccUniverse { universe: max_universe, region: None };
|
let dummy_scc_universe = SccUniverse { universe: max_universe, region: None };
|
||||||
Self {
|
Self {
|
||||||
tcx,
|
tcx,
|
||||||
universe_at_start_of_snapshot,
|
outer_universe,
|
||||||
mini_graph,
|
mini_graph,
|
||||||
rcc,
|
rcc,
|
||||||
scc_placeholders: IndexVec::from_elem_n(None, mini_graph.sccs.num_sccs()),
|
scc_placeholders: IndexVec::from_elem_n(None, mini_graph.sccs.num_sccs()),
|
||||||
|
@ -154,7 +153,7 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
|
||||||
|
|
||||||
// Detect those SCCs that directly contain a placeholder
|
// Detect those SCCs that directly contain a placeholder
|
||||||
if let ty::RePlaceholder(placeholder) = **region {
|
if let ty::RePlaceholder(placeholder) = **region {
|
||||||
if self.universe_at_start_of_snapshot.cannot_name(placeholder.universe) {
|
if self.outer_universe.cannot_name(placeholder.universe) {
|
||||||
self.assign_scc_value(scc, placeholder)?;
|
self.assign_scc_value(scc, placeholder)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -364,58 +363,72 @@ struct MiniGraph<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> MiniGraph<'tcx> {
|
impl<'tcx> MiniGraph<'tcx> {
|
||||||
fn new<'a>(
|
fn new(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
undo_log: impl Iterator<Item = &'a UndoLog<'tcx>>,
|
region_constraints: &RegionConstraintCollector<'_, 'tcx>,
|
||||||
verifys: &[Verify<'tcx>],
|
only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
|
||||||
) -> Self
|
) -> Self {
|
||||||
where
|
|
||||||
'tcx: 'a,
|
|
||||||
{
|
|
||||||
let mut nodes = FxIndexMap::default();
|
let mut nodes = FxIndexMap::default();
|
||||||
let mut edges = Vec::new();
|
let mut edges = Vec::new();
|
||||||
|
|
||||||
// Note that if `R2: R1`, we get a callback `r1, r2`, so `target` is first parameter.
|
// Note that if `R2: R1`, we get a callback `r1, r2`, so `target` is first parameter.
|
||||||
Self::iterate_undo_log(tcx, undo_log, verifys, |target, source| {
|
Self::iterate_region_constraints(
|
||||||
|
tcx,
|
||||||
|
region_constraints,
|
||||||
|
only_consider_snapshot,
|
||||||
|
|target, source| {
|
||||||
let source_node = Self::add_node(&mut nodes, source);
|
let source_node = Self::add_node(&mut nodes, source);
|
||||||
let target_node = Self::add_node(&mut nodes, target);
|
let target_node = Self::add_node(&mut nodes, target);
|
||||||
edges.push((source_node, target_node));
|
edges.push((source_node, target_node));
|
||||||
});
|
},
|
||||||
|
);
|
||||||
let graph = VecGraph::new(nodes.len(), edges);
|
let graph = VecGraph::new(nodes.len(), edges);
|
||||||
let sccs = Sccs::new(&graph);
|
let sccs = Sccs::new(&graph);
|
||||||
Self { nodes, sccs }
|
Self { nodes, sccs }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invokes `each_edge(R1, R2)` for each edge where `R2: R1`
|
/// Invokes `each_edge(R1, R2)` for each edge where `R2: R1`
|
||||||
fn iterate_undo_log<'a>(
|
fn iterate_region_constraints(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
undo_log: impl Iterator<Item = &'a UndoLog<'tcx>>,
|
region_constraints: &RegionConstraintCollector<'_, 'tcx>,
|
||||||
verifys: &[Verify<'tcx>],
|
only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
|
||||||
mut each_edge: impl FnMut(ty::Region<'tcx>, ty::Region<'tcx>),
|
mut each_edge: impl FnMut(ty::Region<'tcx>, ty::Region<'tcx>),
|
||||||
) where
|
) {
|
||||||
'tcx: 'a,
|
let mut each_constraint = |constraint| match constraint {
|
||||||
{
|
&Constraint::VarSubVar(a, b) => {
|
||||||
for undo_entry in undo_log {
|
|
||||||
match undo_entry {
|
|
||||||
&AddConstraint(Constraint::VarSubVar(a, b)) => {
|
|
||||||
each_edge(ty::Region::new_var(tcx, a), ty::Region::new_var(tcx, b));
|
each_edge(ty::Region::new_var(tcx, a), ty::Region::new_var(tcx, b));
|
||||||
}
|
}
|
||||||
&AddConstraint(Constraint::RegSubVar(a, b)) => {
|
&Constraint::RegSubVar(a, b) => {
|
||||||
each_edge(a, ty::Region::new_var(tcx, b));
|
each_edge(a, ty::Region::new_var(tcx, b));
|
||||||
}
|
}
|
||||||
&AddConstraint(Constraint::VarSubReg(a, b)) => {
|
&Constraint::VarSubReg(a, b) => {
|
||||||
each_edge(ty::Region::new_var(tcx, a), b);
|
each_edge(ty::Region::new_var(tcx, a), b);
|
||||||
}
|
}
|
||||||
&AddConstraint(Constraint::RegSubReg(a, b)) => {
|
&Constraint::RegSubReg(a, b) => {
|
||||||
each_edge(a, b);
|
each_edge(a, b);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(snapshot) = only_consider_snapshot {
|
||||||
|
for undo_entry in
|
||||||
|
region_constraints.undo_log.region_constraints_in_snapshot(&snapshot.undo_snapshot)
|
||||||
|
{
|
||||||
|
match undo_entry {
|
||||||
|
AddConstraint(constraint) => {
|
||||||
|
each_constraint(constraint);
|
||||||
|
}
|
||||||
&AddVerify(i) => span_bug!(
|
&AddVerify(i) => span_bug!(
|
||||||
verifys[i].origin.span(),
|
region_constraints.data().verifys[i].origin.span(),
|
||||||
"we never add verifications while doing higher-ranked things",
|
"we never add verifications while doing higher-ranked things",
|
||||||
),
|
),
|
||||||
&AddCombination(..) | &AddVar(..) => {}
|
&AddCombination(..) | &AddVar(..) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for (constraint, _origin) in ®ion_constraints.data().constraints {
|
||||||
|
each_constraint(constraint)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_node(
|
fn add_node(
|
||||||
|
|
|
@ -400,7 +400,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn data(&self) -> &RegionConstraintData<'tcx> {
|
pub fn data(&self) -> &RegionConstraintData<'tcx> {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,15 +683,10 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See `InferCtxt::region_constraints_added_in_snapshot`.
|
/// See `InferCtxt::region_constraints_added_in_snapshot`.
|
||||||
pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot<'tcx>) -> Option<bool> {
|
pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot<'tcx>) -> bool {
|
||||||
self.undo_log
|
self.undo_log
|
||||||
.region_constraints_in_snapshot(mark)
|
.region_constraints_in_snapshot(mark)
|
||||||
.map(|&elt| match elt {
|
.any(|&elt| matches!(elt, AddConstraint(_)))
|
||||||
AddConstraint(constraint) => Some(constraint.involves_placeholders()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.max()
|
|
||||||
.unwrap_or(None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -183,15 +183,6 @@ impl<'tcx> InferCtxtUndoLogs<'tcx> {
|
||||||
self.logs[s.undo_len..].iter().any(|log| matches!(log, UndoLog::OpaqueTypes(..)))
|
self.logs[s.undo_len..].iter().any(|log| matches!(log, UndoLog::OpaqueTypes(..)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn region_constraints(
|
|
||||||
&self,
|
|
||||||
) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone {
|
|
||||||
self.logs.iter().filter_map(|log| match log {
|
|
||||||
UndoLog::RegionConstraintCollector(log) => Some(log),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_open_snapshot(&self, snapshot: &Snapshot<'tcx>) {
|
fn assert_open_snapshot(&self, snapshot: &Snapshot<'tcx>) {
|
||||||
// Failures here may indicate a failure to follow a stack discipline.
|
// Failures here may indicate a failure to follow a stack discipline.
|
||||||
assert!(self.logs.len() >= snapshot.undo_len);
|
assert!(self.logs.len() >= snapshot.undo_len);
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
//! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html
|
//! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html
|
||||||
|
|
||||||
use crate::infer::outlives::env::OutlivesEnvironment;
|
use crate::infer::outlives::env::OutlivesEnvironment;
|
||||||
use crate::infer::{CombinedSnapshot, InferOk};
|
use crate::infer::InferOk;
|
||||||
use crate::traits::outlives_bounds::InferCtxtExt as _;
|
use crate::traits::outlives_bounds::InferCtxtExt as _;
|
||||||
use crate::traits::select::IntercrateAmbiguityCause;
|
use crate::traits::select::IntercrateAmbiguityCause;
|
||||||
use crate::traits::util::impl_subject_and_oblig;
|
use crate::traits::util::impl_subject_and_oblig;
|
||||||
|
@ -62,6 +62,21 @@ pub fn add_placeholder_note(err: &mut Diagnostic) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum TrackAmbiguityCauses {
|
||||||
|
Yes,
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TrackAmbiguityCauses {
|
||||||
|
fn is_yes(self) -> bool {
|
||||||
|
match self {
|
||||||
|
TrackAmbiguityCauses::Yes => true,
|
||||||
|
TrackAmbiguityCauses::No => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// If there are types that satisfy both impls, returns `Some`
|
/// If there are types that satisfy both impls, returns `Some`
|
||||||
/// with a suitably-freshened `ImplHeader` with those types
|
/// with a suitably-freshened `ImplHeader` with those types
|
||||||
/// substituted. Otherwise, returns `None`.
|
/// substituted. Otherwise, returns `None`.
|
||||||
|
@ -97,29 +112,28 @@ pub fn overlapping_impls(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let infcx = tcx
|
let _overlap_with_bad_diagnostics = overlap(
|
||||||
.infer_ctxt()
|
tcx,
|
||||||
.with_opaque_type_inference(DefiningAnchor::Bubble)
|
TrackAmbiguityCauses::No,
|
||||||
.intercrate(true)
|
skip_leak_check,
|
||||||
.build();
|
impl1_def_id,
|
||||||
let selcx = &mut SelectionContext::new(&infcx);
|
impl2_def_id,
|
||||||
let overlaps =
|
overlap_mode,
|
||||||
overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some();
|
)?;
|
||||||
if !overlaps {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In the case where we detect an error, run the check again, but
|
// In the case where we detect an error, run the check again, but
|
||||||
// this time tracking intercrate ambiguity causes for better
|
// this time tracking intercrate ambiguity causes for better
|
||||||
// diagnostics. (These take time and can lead to false errors.)
|
// diagnostics. (These take time and can lead to false errors.)
|
||||||
let infcx = tcx
|
let overlap = overlap(
|
||||||
.infer_ctxt()
|
tcx,
|
||||||
.with_opaque_type_inference(DefiningAnchor::Bubble)
|
TrackAmbiguityCauses::Yes,
|
||||||
.intercrate(true)
|
skip_leak_check,
|
||||||
.build();
|
impl1_def_id,
|
||||||
let selcx = &mut SelectionContext::new(&infcx);
|
impl2_def_id,
|
||||||
selcx.enable_tracking_intercrate_ambiguity_causes();
|
overlap_mode,
|
||||||
Some(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap())
|
)
|
||||||
|
.unwrap();
|
||||||
|
Some(overlap)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_fresh_ty_vars<'cx, 'tcx>(
|
fn with_fresh_ty_vars<'cx, 'tcx>(
|
||||||
|
@ -146,40 +160,34 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
|
||||||
|
|
||||||
/// Can both impl `a` and impl `b` be satisfied by a common type (including
|
/// Can both impl `a` and impl `b` be satisfied by a common type (including
|
||||||
/// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls.
|
/// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls.
|
||||||
fn overlap<'cx, 'tcx>(
|
#[instrument(level = "debug", skip(tcx))]
|
||||||
selcx: &mut SelectionContext<'cx, 'tcx>,
|
fn overlap<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
track_ambiguity_causes: TrackAmbiguityCauses,
|
||||||
skip_leak_check: SkipLeakCheck,
|
skip_leak_check: SkipLeakCheck,
|
||||||
impl1_def_id: DefId,
|
impl1_def_id: DefId,
|
||||||
impl2_def_id: DefId,
|
impl2_def_id: DefId,
|
||||||
overlap_mode: OverlapMode,
|
overlap_mode: OverlapMode,
|
||||||
) -> Option<OverlapResult<'tcx>> {
|
) -> Option<OverlapResult<'tcx>> {
|
||||||
debug!(
|
|
||||||
"overlap(impl1_def_id={:?}, impl2_def_id={:?}, overlap_mode={:?})",
|
|
||||||
impl1_def_id, impl2_def_id, overlap_mode
|
|
||||||
);
|
|
||||||
|
|
||||||
selcx.infcx.probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| {
|
|
||||||
overlap_within_probe(selcx, impl1_def_id, impl2_def_id, overlap_mode, snapshot)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overlap_within_probe<'cx, 'tcx>(
|
|
||||||
selcx: &mut SelectionContext<'cx, 'tcx>,
|
|
||||||
impl1_def_id: DefId,
|
|
||||||
impl2_def_id: DefId,
|
|
||||||
overlap_mode: OverlapMode,
|
|
||||||
snapshot: &CombinedSnapshot<'tcx>,
|
|
||||||
) -> Option<OverlapResult<'tcx>> {
|
|
||||||
let infcx = selcx.infcx;
|
|
||||||
|
|
||||||
if overlap_mode.use_negative_impl() {
|
if overlap_mode.use_negative_impl() {
|
||||||
if negative_impl(infcx.tcx, impl1_def_id, impl2_def_id)
|
if negative_impl(tcx, impl1_def_id, impl2_def_id)
|
||||||
|| negative_impl(infcx.tcx, impl2_def_id, impl1_def_id)
|
|| negative_impl(tcx, impl2_def_id, impl1_def_id)
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let infcx = tcx
|
||||||
|
.infer_ctxt()
|
||||||
|
.with_opaque_type_inference(DefiningAnchor::Bubble)
|
||||||
|
.skip_leak_check(skip_leak_check.is_yes())
|
||||||
|
.intercrate(true)
|
||||||
|
.build();
|
||||||
|
let selcx = &mut SelectionContext::new(&infcx);
|
||||||
|
if track_ambiguity_causes.is_yes() {
|
||||||
|
selcx.enable_tracking_intercrate_ambiguity_causes();
|
||||||
|
}
|
||||||
|
|
||||||
// For the purposes of this check, we don't bring any placeholder
|
// For the purposes of this check, we don't bring any placeholder
|
||||||
// types into scope; instead, we replace the generic types with
|
// types into scope; instead, we replace the generic types with
|
||||||
// fresh type variables, and hence we do our evaluations in an
|
// fresh type variables, and hence we do our evaluations in an
|
||||||
|
@ -198,18 +206,23 @@ fn overlap_within_probe<'cx, 'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We disable the leak when creating the `snapshot` by using
|
// We toggle the `leak_check` by using `skip_leak_check` when constructing the
|
||||||
// `infcx.probe_maybe_disable_leak_check`.
|
// inference context, so this may be a noop.
|
||||||
if infcx.leak_check(snapshot).is_err() {
|
if infcx.leak_check(ty::UniverseIndex::ROOT, None).is_err() {
|
||||||
debug!("overlap: leak check failed");
|
debug!("overlap: leak check failed");
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes();
|
let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes();
|
||||||
debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes);
|
debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes);
|
||||||
|
let involves_placeholder = infcx
|
||||||
let involves_placeholder =
|
.inner
|
||||||
matches!(selcx.infcx.region_constraints_added_in_snapshot(snapshot), Some(true));
|
.borrow_mut()
|
||||||
|
.unwrap_region_constraints()
|
||||||
|
.data()
|
||||||
|
.constraints
|
||||||
|
.iter()
|
||||||
|
.any(|c| c.0.involves_placeholders());
|
||||||
|
|
||||||
let impl_header = selcx.infcx.resolve_vars_if_possible(impl1_header);
|
let impl_header = selcx.infcx.resolve_vars_if_possible(impl1_header);
|
||||||
Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder })
|
Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder })
|
||||||
|
|
|
@ -90,7 +90,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
|
||||||
Ok(EvaluationResult::EvaluatedToAmbig)
|
Ok(EvaluationResult::EvaluatedToAmbig)
|
||||||
} else if self.opaque_types_added_in_snapshot(snapshot) {
|
} else if self.opaque_types_added_in_snapshot(snapshot) {
|
||||||
Ok(EvaluationResult::EvaluatedToOkModuloOpaqueTypes)
|
Ok(EvaluationResult::EvaluatedToOkModuloOpaqueTypes)
|
||||||
} else if self.region_constraints_added_in_snapshot(snapshot).is_some() {
|
} else if self.region_constraints_added_in_snapshot(snapshot) {
|
||||||
Ok(EvaluationResult::EvaluatedToOkModuloRegions)
|
Ok(EvaluationResult::EvaluatedToOkModuloRegions)
|
||||||
} else {
|
} else {
|
||||||
Ok(EvaluationResult::EvaluatedToOk)
|
Ok(EvaluationResult::EvaluatedToOk)
|
||||||
|
|
|
@ -561,9 +561,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
op: impl FnOnce(&mut Self) -> Result<EvaluationResult, OverflowError>,
|
op: impl FnOnce(&mut Self) -> Result<EvaluationResult, OverflowError>,
|
||||||
) -> Result<EvaluationResult, OverflowError> {
|
) -> Result<EvaluationResult, OverflowError> {
|
||||||
self.infcx.probe(|snapshot| -> Result<EvaluationResult, OverflowError> {
|
self.infcx.probe(|snapshot| -> Result<EvaluationResult, OverflowError> {
|
||||||
|
let outer_universe = self.infcx.universe();
|
||||||
let result = op(self)?;
|
let result = op(self)?;
|
||||||
|
|
||||||
match self.infcx.leak_check(snapshot) {
|
match self.infcx.leak_check(outer_universe, Some(snapshot)) {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(_) => return Ok(EvaluatedToErr),
|
Err(_) => return Ok(EvaluatedToErr),
|
||||||
}
|
}
|
||||||
|
@ -572,9 +573,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
return Ok(result.max(EvaluatedToOkModuloOpaqueTypes));
|
return Ok(result.max(EvaluatedToOkModuloOpaqueTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.infcx.region_constraints_added_in_snapshot(snapshot) {
|
if self.infcx.region_constraints_added_in_snapshot(snapshot) {
|
||||||
None => Ok(result),
|
Ok(result.max(EvaluatedToOkModuloRegions))
|
||||||
Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)),
|
} else {
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue