1
Fork 0

change snapshot tracking in fulfillment contexts

This commit is contained in:
lcnr 2023-06-29 10:02:26 +02:00
parent 46973c9c8a
commit d04775d739
23 changed files with 79 additions and 121 deletions

View file

@ -1974,7 +1974,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.copied() .copied()
.filter(|&(impl_, _)| { .filter(|&(impl_, _)| {
infcx.probe(|_| { infcx.probe(|_| {
let ocx = ObligationCtxt::new_in_snapshot(&infcx); let ocx = ObligationCtxt::new(&infcx);
ocx.register_obligations(obligations.clone()); ocx.register_obligations(obligations.clone());
let impl_substs = infcx.fresh_substs_for_item(span, impl_); let impl_substs = infcx.fresh_substs_for_item(span, impl_);

View file

@ -161,7 +161,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
&self, &self,
ty: Ty<'tcx>, ty: Ty<'tcx>,
) -> Option<(Ty<'tcx>, Vec<traits::PredicateObligation<'tcx>>)> { ) -> Option<(Ty<'tcx>, Vec<traits::PredicateObligation<'tcx>>)> {
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new_in_snapshot(self.infcx); let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self.infcx);
let cause = traits::ObligationCause::misc(self.span, self.body_id); let cause = traits::ObligationCause::misc(self.span, self.body_id);
let normalized_ty = match self let normalized_ty = match self

View file

@ -1282,7 +1282,7 @@ fn suggest_impl_trait<'tcx>(
{ {
continue; continue;
} }
let ocx = ObligationCtxt::new_in_snapshot(&infcx); let ocx = ObligationCtxt::new(&infcx);
let item_ty = ocx.normalize( let item_ty = ocx.normalize(
&ObligationCause::misc(span, def_id), &ObligationCause::misc(span, def_id),
param_env, param_env,

View file

@ -1033,7 +1033,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let Ok(ok) = coerce.coerce(source, target) else { let Ok(ok) = coerce.coerce(source, target) else {
return false; return false;
}; };
let ocx = ObligationCtxt::new_in_snapshot(self); let ocx = ObligationCtxt::new(self);
ocx.register_obligations(ok.obligations); ocx.register_obligations(ok.obligations);
ocx.select_where_possible().is_empty() ocx.select_where_possible().is_empty()
}) })

View file

@ -2918,7 +2918,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}; };
self.commit_if_ok(|_| { self.commit_if_ok(|_| {
let ocx = ObligationCtxt::new_in_snapshot(self); let ocx = ObligationCtxt::new(self);
let impl_substs = self.fresh_substs_for_item(base_expr.span, impl_def_id); let impl_substs = self.fresh_substs_for_item(base_expr.span, impl_def_id);
let impl_trait_ref = let impl_trait_ref =
self.tcx.impl_trait_ref(impl_def_id).unwrap().subst(self.tcx, impl_substs); self.tcx.impl_trait_ref(impl_def_id).unwrap().subst(self.tcx, impl_substs);

View file

@ -746,7 +746,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let expect_args = self let expect_args = self
.fudge_inference_if_ok(|| { .fudge_inference_if_ok(|| {
let ocx = ObligationCtxt::new_in_snapshot(self); let ocx = ObligationCtxt::new(self);
// Attempt to apply a subtyping relationship between the formal // Attempt to apply a subtyping relationship between the formal
// return type (likely containing type variables if the function // return type (likely containing type variables if the function

View file

@ -163,7 +163,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return fn_sig; return fn_sig;
} }
self.probe(|_| { self.probe(|_| {
let ocx = ObligationCtxt::new_in_snapshot(self); let ocx = ObligationCtxt::new(self);
let normalized_fn_sig = let normalized_fn_sig =
ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig); ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig);
if ocx.select_all_or_error().is_empty() { if ocx.select_all_or_error().is_empty() {

View file

@ -79,7 +79,6 @@ impl<'tcx> InferCtxt<'tcx> {
reported_closure_mismatch: self.reported_closure_mismatch.clone(), reported_closure_mismatch: self.reported_closure_mismatch.clone(),
tainted_by_errors: self.tainted_by_errors.clone(), tainted_by_errors: self.tainted_by_errors.clone(),
err_count_on_creation: self.err_count_on_creation, err_count_on_creation: self.err_count_on_creation,
in_snapshot: self.in_snapshot.clone(),
universe: self.universe.clone(), universe: self.universe.clone(),
intercrate: self.intercrate, intercrate: self.intercrate,
next_trait_solver: self.next_trait_solver, next_trait_solver: self.next_trait_solver,

View file

@ -6,6 +6,7 @@ pub use self::RegionVariableOrigin::*;
pub use self::SubregionOrigin::*; pub use self::SubregionOrigin::*;
pub use self::ValuePairs::*; pub use self::ValuePairs::*;
pub use combine::ObligationEmittingRelation; pub use combine::ObligationEmittingRelation;
use rustc_data_structures::undo_log::UndoLogs;
use self::opaque_types::OpaqueTypeStorage; use self::opaque_types::OpaqueTypeStorage;
pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog}; pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog};
@ -297,9 +298,6 @@ pub struct InferCtxt<'tcx> {
// FIXME(matthewjasper) Merge into `tainted_by_errors` // FIXME(matthewjasper) Merge into `tainted_by_errors`
err_count_on_creation: usize, err_count_on_creation: usize,
/// This flag is true while there is an active snapshot.
in_snapshot: Cell<bool>,
/// What is the innermost universe we have created? Starts out as /// What is the innermost universe we have created? Starts out as
/// `UniverseIndex::root()` but grows from there as we enter /// `UniverseIndex::root()` but grows from there as we enter
/// universal quantifiers. /// universal quantifiers.
@ -643,7 +641,6 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
reported_closure_mismatch: Default::default(), reported_closure_mismatch: Default::default(),
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),
universe: Cell::new(ty::UniverseIndex::ROOT), universe: Cell::new(ty::UniverseIndex::ROOT),
intercrate, intercrate,
next_trait_solver, next_trait_solver,
@ -679,7 +676,6 @@ pub struct CombinedSnapshot<'tcx> {
undo_snapshot: Snapshot<'tcx>, undo_snapshot: Snapshot<'tcx>,
region_constraints_snapshot: RegionSnapshot, region_constraints_snapshot: RegionSnapshot,
universe: ty::UniverseIndex, universe: ty::UniverseIndex,
was_in_snapshot: bool,
} }
impl<'tcx> InferCtxt<'tcx> { impl<'tcx> InferCtxt<'tcx> {
@ -702,10 +698,6 @@ impl<'tcx> InferCtxt<'tcx> {
} }
} }
pub fn is_in_snapshot(&self) -> bool {
self.in_snapshot.get()
}
pub fn freshen<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T { pub fn freshen<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T {
t.fold_with(&mut self.freshener()) t.fold_with(&mut self.freshener())
} }
@ -766,31 +758,30 @@ impl<'tcx> InferCtxt<'tcx> {
} }
} }
pub fn in_snapshot(&self) -> bool {
UndoLogs::<UndoLog<'tcx>>::in_snapshot(&self.inner.borrow_mut().undo_log)
}
pub fn num_open_snapshots(&self) -> usize {
UndoLogs::<UndoLog<'tcx>>::num_open_snapshots(&self.inner.borrow_mut().undo_log)
}
fn start_snapshot(&self) -> CombinedSnapshot<'tcx> { fn start_snapshot(&self) -> CombinedSnapshot<'tcx> {
debug!("start_snapshot()"); debug!("start_snapshot()");
let in_snapshot = self.in_snapshot.replace(true);
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.borrow_mut();
CombinedSnapshot { CombinedSnapshot {
undo_snapshot: inner.undo_log.start_snapshot(), undo_snapshot: inner.undo_log.start_snapshot(),
region_constraints_snapshot: inner.unwrap_region_constraints().start_snapshot(), region_constraints_snapshot: inner.unwrap_region_constraints().start_snapshot(),
universe: self.universe(), universe: self.universe(),
was_in_snapshot: in_snapshot,
} }
} }
#[instrument(skip(self, snapshot), level = "debug")] #[instrument(skip(self, snapshot), level = "debug")]
fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'tcx>) { fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'tcx>) {
let CombinedSnapshot { let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe } = snapshot;
undo_snapshot,
region_constraints_snapshot,
universe,
was_in_snapshot,
} = snapshot;
self.in_snapshot.set(was_in_snapshot);
self.universe.set(universe); self.universe.set(universe);
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.borrow_mut();
@ -800,14 +791,8 @@ impl<'tcx> InferCtxt<'tcx> {
#[instrument(skip(self, snapshot), level = "debug")] #[instrument(skip(self, snapshot), level = "debug")]
fn commit_from(&self, snapshot: CombinedSnapshot<'tcx>) { fn commit_from(&self, snapshot: CombinedSnapshot<'tcx>) {
let CombinedSnapshot { let CombinedSnapshot { undo_snapshot, region_constraints_snapshot: _, universe: _ } =
undo_snapshot, snapshot;
region_constraints_snapshot: _,
universe: _,
was_in_snapshot,
} = snapshot;
self.in_snapshot.set(was_in_snapshot);
self.inner.borrow_mut().commit(undo_snapshot); self.inner.borrow_mut().commit(undo_snapshot);
} }

View file

@ -125,10 +125,7 @@ impl<'tcx> InferCtxt<'tcx> {
/// right before lexical region resolution. /// right before lexical region resolution.
#[instrument(level = "debug", skip(self, outlives_env))] #[instrument(level = "debug", skip(self, outlives_env))]
pub fn process_registered_region_obligations(&self, outlives_env: &OutlivesEnvironment<'tcx>) { pub fn process_registered_region_obligations(&self, outlives_env: &OutlivesEnvironment<'tcx>) {
assert!( assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot");
!self.in_snapshot.get(),
"cannot process registered region obligations in a snapshot"
);
let my_region_obligations = self.take_registered_region_obligations(); let my_region_obligations = self.take_registered_region_obligations();

View file

@ -25,20 +25,27 @@ use super::{Certainty, InferCtxtEvalExt};
/// here as this will have to deal with far more root goals than `evaluate_all`. /// here as this will have to deal with far more root goals than `evaluate_all`.
pub struct FulfillmentCtxt<'tcx> { pub struct FulfillmentCtxt<'tcx> {
obligations: Vec<PredicateObligation<'tcx>>, obligations: Vec<PredicateObligation<'tcx>>,
/// The snapshot in which this context was created. Using the context
/// outside of this snapshot leads to subtle bugs if the snapshot
/// gets rolled back. Because of this we explicitly check that we only
/// use the context in exactly this snapshot.
usable_in_snapshot: usize,
} }
impl<'tcx> FulfillmentCtxt<'tcx> { impl<'tcx> FulfillmentCtxt<'tcx> {
pub fn new() -> FulfillmentCtxt<'tcx> { pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> {
FulfillmentCtxt { obligations: Vec::new() } FulfillmentCtxt { obligations: Vec::new(), usable_in_snapshot: infcx.num_open_snapshots() }
} }
} }
impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
fn register_predicate_obligation( fn register_predicate_obligation(
&mut self, &mut self,
_infcx: &InferCtxt<'tcx>, infcx: &InferCtxt<'tcx>,
obligation: PredicateObligation<'tcx>, obligation: PredicateObligation<'tcx>,
) { ) {
assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
self.obligations.push(obligation); self.obligations.push(obligation);
} }
@ -72,6 +79,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
} }
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
let mut errors = Vec::new(); let mut errors = Vec::new();
for i in 0.. { for i in 0.. {
if !infcx.tcx.recursion_limit().value_within_limit(i) { if !infcx.tcx.recursion_limit().value_within_limit(i) {

View file

@ -13,16 +13,19 @@ use rustc_middle::ty::TypeVisitableExt;
pub struct FulfillmentContext<'tcx> { pub struct FulfillmentContext<'tcx> {
obligations: FxIndexSet<PredicateObligation<'tcx>>, obligations: FxIndexSet<PredicateObligation<'tcx>>,
usable_in_snapshot: bool, /// The snapshot in which this context was created. Using the context
/// outside of this snapshot leads to subtle bugs if the snapshot
/// gets rolled back. Because of this we explicitly check that we only
/// use the context in exactly this snapshot.
usable_in_snapshot: usize,
} }
impl FulfillmentContext<'_> { impl<'tcx> FulfillmentContext<'tcx> {
pub(super) fn new() -> Self { pub(super) fn new(infcx: &InferCtxt<'tcx>) -> Self {
FulfillmentContext { obligations: FxIndexSet::default(), usable_in_snapshot: false } FulfillmentContext {
obligations: FxIndexSet::default(),
usable_in_snapshot: infcx.num_open_snapshots(),
} }
pub(crate) fn new_in_snapshot() -> Self {
FulfillmentContext { usable_in_snapshot: true, ..Self::new() }
} }
} }
@ -32,9 +35,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
infcx: &InferCtxt<'tcx>, infcx: &InferCtxt<'tcx>,
obligation: PredicateObligation<'tcx>, obligation: PredicateObligation<'tcx>,
) { ) {
if !self.usable_in_snapshot { assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
assert!(!infcx.is_in_snapshot());
}
let obligation = infcx.resolve_vars_if_possible(obligation); let obligation = infcx.resolve_vars_if_possible(obligation);
self.obligations.insert(obligation); self.obligations.insert(obligation);
@ -58,9 +59,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
} }
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
if !self.usable_in_snapshot { assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
assert!(!infcx.is_in_snapshot());
}
let mut errors = Vec::new(); let mut errors = Vec::new();
let mut next_round = FxIndexSet::default(); let mut next_round = FxIndexSet::default();
@ -94,12 +93,11 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
&orig_values, &orig_values,
&response, &response,
) { ) {
Ok(infer_ok) => next_round.extend( Ok(infer_ok) => {
infer_ok.obligations.into_iter().map(|obligation| { next_round.extend(infer_ok.obligations.into_iter().map(
assert!(!infcx.is_in_snapshot()); |obligation| infcx.resolve_vars_if_possible(obligation),
infcx.resolve_vars_if_possible(obligation) ))
}), }
),
Err(_err) => errors.push(FulfillmentError { Err(_err) => errors.push(FulfillmentError {
obligation: obligation.clone(), obligation: obligation.clone(),

View file

@ -176,7 +176,7 @@ fn satisfied_from_param_env<'tcx>(
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
debug!("is_const_evaluatable: candidate={:?}", c); debug!("is_const_evaluatable: candidate={:?}", c);
if self.infcx.probe(|_| { if self.infcx.probe(|_| {
let ocx = ObligationCtxt::new_in_snapshot(self.infcx); let ocx = ObligationCtxt::new(self.infcx);
ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty()).is_ok() ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty()).is_ok()
&& ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok() && ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok()
&& ocx.select_all_or_error().is_empty() && ocx.select_all_or_error().is_empty()
@ -219,7 +219,7 @@ fn satisfied_from_param_env<'tcx>(
} }
if let Some(Ok(c)) = single_match { if let Some(Ok(c)) = single_match {
let ocx = ObligationCtxt::new_in_snapshot(infcx); let ocx = ObligationCtxt::new(infcx);
assert!(ocx.eq(&ObligationCause::dummy(), param_env, c.ty(), ct.ty()).is_ok()); assert!(ocx.eq(&ObligationCause::dummy(), param_env, c.ty(), ct.ty()).is_ok());
assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok()); assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok());
assert!(ocx.select_all_or_error().is_empty()); assert!(ocx.select_all_or_error().is_empty());

View file

@ -28,36 +28,18 @@ use rustc_span::Span;
pub trait TraitEngineExt<'tcx> { pub trait TraitEngineExt<'tcx> {
fn new(infcx: &InferCtxt<'tcx>) -> Box<Self>; fn new(infcx: &InferCtxt<'tcx>) -> Box<Self>;
fn new_in_snapshot(infcx: &InferCtxt<'tcx>) -> Box<Self>;
} }
impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
fn new(infcx: &InferCtxt<'tcx>) -> Box<Self> { fn new(infcx: &InferCtxt<'tcx>) -> Box<Self> {
match (infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.next_trait_solver()) { match (infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.next_trait_solver()) {
(TraitSolver::Classic, false) | (TraitSolver::NextCoherence, false) => { (TraitSolver::Classic, false) | (TraitSolver::NextCoherence, false) => {
Box::new(FulfillmentContext::new()) Box::new(FulfillmentContext::new(infcx))
} }
(TraitSolver::Next | TraitSolver::NextCoherence, true) => { (TraitSolver::Next | TraitSolver::NextCoherence, true) => {
Box::new(NextFulfillmentCtxt::new()) Box::new(NextFulfillmentCtxt::new(infcx))
} }
(TraitSolver::Chalk, false) => Box::new(ChalkFulfillmentContext::new()), (TraitSolver::Chalk, false) => Box::new(ChalkFulfillmentContext::new(infcx)),
_ => bug!(
"incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})",
infcx.tcx.sess.opts.unstable_opts.trait_solver,
infcx.next_trait_solver()
),
}
}
fn new_in_snapshot(infcx: &InferCtxt<'tcx>) -> Box<Self> {
match (infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.next_trait_solver()) {
(TraitSolver::Classic, false) | (TraitSolver::NextCoherence, false) => {
Box::new(FulfillmentContext::new_in_snapshot())
}
(TraitSolver::Next | TraitSolver::NextCoherence, true) => {
Box::new(NextFulfillmentCtxt::new())
}
(TraitSolver::Chalk, false) => Box::new(ChalkFulfillmentContext::new_in_snapshot()),
_ => bug!( _ => bug!(
"incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})", "incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})",
infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.tcx.sess.opts.unstable_opts.trait_solver,
@ -79,10 +61,6 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx)) } Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx)) }
} }
pub fn new_in_snapshot(infcx: &'a InferCtxt<'tcx>) -> Self {
Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new_in_snapshot(infcx)) }
}
pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) { pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) {
self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation); self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation);
} }

View file

@ -20,7 +20,7 @@ pub fn recompute_applicable_impls<'tcx>(
let param_env = obligation.param_env; let param_env = obligation.param_env;
let impl_may_apply = |impl_def_id| { let impl_may_apply = |impl_def_id| {
let ocx = ObligationCtxt::new_in_snapshot(infcx); let ocx = ObligationCtxt::new(infcx);
let placeholder_obligation = let placeholder_obligation =
infcx.instantiate_binder_with_placeholders(obligation.predicate); infcx.instantiate_binder_with_placeholders(obligation.predicate);
let obligation_trait_ref = let obligation_trait_ref =
@ -45,7 +45,7 @@ pub fn recompute_applicable_impls<'tcx>(
}; };
let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| { let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| {
let ocx = ObligationCtxt::new_in_snapshot(infcx); let ocx = ObligationCtxt::new(infcx);
let placeholder_obligation = let placeholder_obligation =
infcx.instantiate_binder_with_placeholders(obligation.predicate); infcx.instantiate_binder_with_placeholders(obligation.predicate);
let obligation_trait_ref = let obligation_trait_ref =

View file

@ -377,7 +377,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
param_env, param_env,
ty.rebind(ty::TraitPredicate { trait_ref, constness, polarity }), ty.rebind(ty::TraitPredicate { trait_ref, constness, polarity }),
); );
let ocx = ObligationCtxt::new_in_snapshot(self); let ocx = ObligationCtxt::new(self);
ocx.register_obligation(obligation); ocx.register_obligation(obligation);
if ocx.select_all_or_error().is_empty() { if ocx.select_all_or_error().is_empty() {
return Ok(( return Ok((
@ -1596,7 +1596,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
} }
self.probe(|_| { self.probe(|_| {
let ocx = ObligationCtxt::new_in_snapshot(self); let ocx = ObligationCtxt::new(self);
// try to find the mismatched types to report the error with. // try to find the mismatched types to report the error with.
// //

View file

@ -3799,7 +3799,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
body_id: hir::HirId, body_id: hir::HirId,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>> { ) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>> {
let ocx = ObligationCtxt::new_in_snapshot(self.infcx); let ocx = ObligationCtxt::new(self.infcx);
let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len()); let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len());
for diff in type_diffs { for diff in type_diffs {
let Sorts(expected_found) = diff else { continue; }; let Sorts(expected_found) = diff else { continue; };

View file

@ -50,20 +50,15 @@ impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
/// method `select_all_or_error` can be used to report any remaining /// method `select_all_or_error` can be used to report any remaining
/// ambiguous cases as errors. /// ambiguous cases as errors.
pub struct FulfillmentContext<'tcx> { pub struct FulfillmentContext<'tcx> {
// A list of all obligations that have been registered with this /// A list of all obligations that have been registered with this
// fulfillment context. /// fulfillment context.
predicates: ObligationForest<PendingPredicateObligation<'tcx>>, predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
// Is it OK to register obligations into this infcx inside /// The snapshot in which this context was created. Using the context
// an infcx snapshot? /// outside of this snapshot leads to subtle bugs if the snapshot
// /// gets rolled back. Because of this we explicitly check that we only
// The "primary fulfillment" in many cases in typeck lives /// use the context in exactly this snapshot.
// outside of any snapshot, so any use of it inside a snapshot usable_in_snapshot: usize,
// will lead to trouble and therefore is checked against, but
// other fulfillment contexts sometimes do live inside of
// a snapshot (they don't *straddle* a snapshot, so there
// is no trouble there).
usable_in_snapshot: bool,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -80,18 +75,17 @@ pub struct PendingPredicateObligation<'tcx> {
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(PendingPredicateObligation<'_>, 72); static_assert_size!(PendingPredicateObligation<'_>, 72);
impl<'a, 'tcx> FulfillmentContext<'tcx> { impl<'tcx> FulfillmentContext<'tcx> {
/// Creates a new fulfillment context. /// Creates a new fulfillment context.
pub(super) fn new() -> FulfillmentContext<'tcx> { pub(super) fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentContext<'tcx> {
FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: false } FulfillmentContext {
predicates: ObligationForest::new(),
usable_in_snapshot: infcx.num_open_snapshots(),
} }
pub(super) fn new_in_snapshot() -> FulfillmentContext<'tcx> {
FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: true }
} }
/// Attempts to select obligations using `selcx`. /// Attempts to select obligations using `selcx`.
fn select(&mut self, selcx: SelectionContext<'a, 'tcx>) -> Vec<FulfillmentError<'tcx>> { fn select(&mut self, selcx: SelectionContext<'_, 'tcx>) -> Vec<FulfillmentError<'tcx>> {
let span = debug_span!("select", obligation_forest_size = ?self.predicates.len()); let span = debug_span!("select", obligation_forest_size = ?self.predicates.len());
let _enter = span.enter(); let _enter = span.enter();
@ -122,14 +116,13 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
infcx: &InferCtxt<'tcx>, infcx: &InferCtxt<'tcx>,
obligation: PredicateObligation<'tcx>, obligation: PredicateObligation<'tcx>,
) { ) {
assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
// this helps to reduce duplicate errors, as well as making // this helps to reduce duplicate errors, as well as making
// debug output much nicer to read and so on. // debug output much nicer to read and so on.
let obligation = infcx.resolve_vars_if_possible(obligation); let obligation = infcx.resolve_vars_if_possible(obligation);
debug!(?obligation, "register_predicate_obligation"); debug!(?obligation, "register_predicate_obligation");
assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot);
self.predicates self.predicates
.register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] }); .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] });
} }

View file

@ -161,7 +161,7 @@ fn pred_known_to_hold_modulo_regions<'tcx>(
// the we do no inference in the process of checking this obligation. // the we do no inference in the process of checking this obligation.
let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env)); let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
infcx.probe(|_| { infcx.probe(|_| {
let ocx = ObligationCtxt::new_in_snapshot(infcx); let ocx = ObligationCtxt::new(infcx);
ocx.register_obligation(obligation); ocx.register_obligation(obligation);
let errors = ocx.select_all_or_error(); let errors = ocx.select_all_or_error();

View file

@ -80,7 +80,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
if self.next_trait_solver() { if self.next_trait_solver() {
self.probe(|snapshot| { self.probe(|snapshot| {
let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(); let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(self);
fulfill_cx.register_predicate_obligation(self, obligation.clone()); fulfill_cx.register_predicate_obligation(self, obligation.clone());
// True errors // True errors
// FIXME(-Ztrait-solver=next): Overflows are reported as ambig here, is that OK? // FIXME(-Ztrait-solver=next): Overflows are reported as ambig here, is that OK?

View file

@ -82,7 +82,7 @@ where
); );
let value = infcx.commit_if_ok(|_| { let value = infcx.commit_if_ok(|_| {
let ocx = ObligationCtxt::new_in_snapshot(infcx); let ocx = ObligationCtxt::new(infcx);
let value = op(&ocx).map_err(|_| { let value = op(&ocx).map_err(|_| {
infcx.tcx.sess.delay_span_bug(span, format!("error performing operation: {name}")) infcx.tcx.sess.delay_span_bug(span, format!("error performing operation: {name}"))
})?; })?;

View file

@ -606,7 +606,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self, &mut self,
predicates: impl IntoIterator<Item = PredicateObligation<'tcx>>, predicates: impl IntoIterator<Item = PredicateObligation<'tcx>>,
) -> Result<EvaluationResult, OverflowError> { ) -> Result<EvaluationResult, OverflowError> {
let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(); let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(self.infcx);
fulfill_cx.register_predicate_obligations(self.infcx, predicates); fulfill_cx.register_predicate_obligations(self.infcx, predicates);
// True errors // True errors
// FIXME(-Ztrait-solver=next): Overflows are reported as ambig here, is that OK? // FIXME(-Ztrait-solver=next): Overflows are reported as ambig here, is that OK?

View file

@ -238,7 +238,7 @@ fn fulfill_implication<'tcx>(
// Needs to be `in_snapshot` because this function is used to rebase // Needs to be `in_snapshot` because this function is used to rebase
// substitutions, which may happen inside of a select within a probe. // substitutions, which may happen inside of a select within a probe.
let ocx = ObligationCtxt::new_in_snapshot(infcx); let ocx = ObligationCtxt::new(infcx);
// attempt to prove all of the predicates for impl2 given those for impl1 // attempt to prove all of the predicates for impl2 given those for impl1
// (which are packed up in penv) // (which are packed up in penv)
ocx.register_obligations(obligations.chain(more_obligations)); ocx.register_obligations(obligations.chain(more_obligations));