1
Fork 0

rework the leak_check to take the outer_universe

clean up coherence to not rely on probes anymore
This commit is contained in:
lcnr 2023-05-23 18:56:25 +02:00
parent 2a4467da9f
commit a0245bb3cb
10 changed files with 178 additions and 166 deletions

View file

@ -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
}) })

View file

@ -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(),

View file

@ -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,
) )
} }
} }

View file

@ -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()

View file

@ -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 &region_constraints.data().constraints {
each_constraint(constraint)
}
}
} }
fn add_node( fn add_node(

View file

@ -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]

View file

@ -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);

View file

@ -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 })

View file

@ -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)

View file

@ -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)
} }
}) })
} }