1use std::marker::PhantomData;
2use std::mem;
34use rustc_infer::infer::InferCtxt;
5use rustc_infer::traits::query::NoSolution;
6use rustc_infer::traits::{
7FromSolverError, PredicateObligation, PredicateObligations, TraitEngine,
8};
9use rustc_middle::ty::{self, TyCtxt, TyVid, TypeVisitableExt, TypingMode};
10use rustc_next_trait_solver::delegate::SolverDelegateas _;
11use rustc_next_trait_solver::solve::{
12GoalEvaluation, GoalStalledOn, HasChanged, MaybeInfo, SolverDelegateEvalExtas _,
13StalledOnCoroutines,
14};
15use thin_vec::ThinVec;
16use tracing::instrument;
1718use self::derive_errors::*;
19use super::Certainty;
20use super::delegate::SolverDelegate;
21use crate::traits::{FulfillmentError, ScrubbedTraitError};
2223mod derive_errors;
2425// FIXME: Do we need to use a `ThinVec` here?
26type PendingObligations<'tcx> =
27ThinVec<(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)>;
2829/// A trait engine using the new trait solver.
30///
31/// This is mostly identical to how `evaluate_all` works inside of the
32/// solver, except that the requirements are slightly different.
33///
34/// Unlike `evaluate_all` it is possible to add new obligations later on
35/// and we also have to track diagnostics information by using `Obligation`
36/// instead of `Goal`.
37///
38/// It is also likely that we want to use slightly different datastructures
39/// here as this will have to deal with far more root goals than `evaluate_all`.
40pub struct FulfillmentCtxt<'tcx, E: 'tcx> {
41 obligations: ObligationStorage<'tcx>,
4243/// The snapshot in which this context was created. Using the context
44 /// outside of this snapshot leads to subtle bugs if the snapshot
45 /// gets rolled back. Because of this we explicitly check that we only
46 /// use the context in exactly this snapshot.
47usable_in_snapshot: usize,
48 _errors: PhantomData<E>,
49}
5051#[derive(#[automatically_derived]
impl<'tcx> ::core::default::Default for ObligationStorage<'tcx> {
#[inline]
fn default() -> ObligationStorage<'tcx> {
ObligationStorage {
overflowed: ::core::default::Default::default(),
pending: ::core::default::Default::default(),
}
}
}Default, #[automatically_derived]
impl<'tcx> ::core::fmt::Debug for ObligationStorage<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f,
"ObligationStorage", "overflowed", &self.overflowed, "pending",
&&self.pending)
}
}Debug)]
52struct ObligationStorage<'tcx> {
53/// Obligations which resulted in an overflow in fulfillment itself.
54 ///
55 /// We cannot eagerly return these as error so we instead store them here
56 /// to avoid recomputing them each time `try_evaluate_obligations` is called.
57 /// This also allows us to return the correct `FulfillmentError` for them.
58overflowed: Vec<PredicateObligation<'tcx>>,
59 pending: PendingObligations<'tcx>,
60}
6162impl<'tcx> ObligationStorage<'tcx> {
63fn register(
64&mut self,
65 obligation: PredicateObligation<'tcx>,
66 stalled_on: Option<GoalStalledOn<TyCtxt<'tcx>>>,
67 ) {
68self.pending.push((obligation, stalled_on));
69 }
7071fn has_pending_obligations(&self) -> bool {
72 !self.pending.is_empty() || !self.overflowed.is_empty()
73 }
7475fn clone_pending(&self) -> PredicateObligations<'tcx> {
76let mut obligations: PredicateObligations<'tcx> =
77self.pending.iter().map(|(o, _)| o.clone()).collect();
78obligations.extend(self.overflowed.iter().cloned());
79obligations80 }
8182fn clone_pending_potentially_referencing_sub_root(
83&self,
84 infcx: &InferCtxt<'tcx>,
85 vid: TyVid,
86 ) -> PredicateObligations<'tcx> {
87let mut obligations: PredicateObligations<'tcx> = self88 .pending
89 .iter()
90 .filter(|(_, stalled_on)| {
91let Some(stalled_on) = stalled_onelse { return true };
92// Don't reuse the sub-unification roots cached on `stalled_on`:
93 // a later sub-unification merge can have changed which root
94 // each stalled var belongs to, so the cached info can be stale.
95 // Walk `stalled_vars` and recompute the current root instead.
96 //
97 // Conservative here: if a stalled var no longer resolves to an
98 // infer var, some unification happened, so the goal is no longer
99 // stalled. Include it to be re-evaluated downstream.
100stalled_on.stalled_vars.iter().filter_map(|arg| arg.as_type()).any(
101 |ty| match *infcx.shallow_resolve(ty).kind() {
102 ty::Infer(ty::TyVar(tv)) => infcx.sub_unification_table_root_var(tv) == vid,
103_ => true,
104 },
105 )
106 })
107 .map(|(o, _)| o.clone())
108 .collect();
109obligations.extend(self.overflowed.iter().cloned());
110obligations111 }
112113fn drain_pending(
114&mut self,
115 cond: impl Fn(&PredicateObligation<'tcx>, &Option<GoalStalledOn<TyCtxt<'tcx>>>) -> bool,
116 ) -> PendingObligations<'tcx> {
117let (unstalled, pending) =
118 mem::take(&mut self.pending).into_iter().partition(|(o, s)| cond(o, s));
119self.pending = pending;
120unstalled121 }
122123fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {
124infcx.probe(|_| {
125// IMPORTANT: we must not use solve any inference variables in the obligations
126 // as this is all happening inside of a probe. We use a probe to make sure
127 // we get all obligations involved in the overflow. We pretty much check: if
128 // we were to do another step of `try_evaluate_obligations`, which goals would
129 // change.
130self.overflowed.extend(
131self.pending
132 .extract_if(.., |(o, stalled_on)| {
133let goal = o.as_goal();
134let result = <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal(
135goal,
136o.cause.span,
137stalled_on.take(),
138 );
139#[allow(non_exhaustive_omitted_patterns)] match result {
Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }) => true,
_ => false,
}matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }))140 })
141 .map(|(o, _)| o),
142 );
143 })
144 }
145}
146147impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
148pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx, E> {
149if !infcx.next_trait_solver() {
{
::core::panicking::panic_fmt(format_args!("new trait solver fulfillment context created when infcx is set up for old trait solver"));
}
};assert!(
150 infcx.next_trait_solver(),
151"new trait solver fulfillment context created when \
152 infcx is set up for old trait solver"
153);
154FulfillmentCtxt {
155 obligations: Default::default(),
156 usable_in_snapshot: infcx.num_open_snapshots(),
157 _errors: PhantomData,
158 }
159 }
160161fn inspect_evaluated_obligation(
162&self,
163 infcx: &InferCtxt<'tcx>,
164 obligation: &PredicateObligation<'tcx>,
165 result: &Result<GoalEvaluation<TyCtxt<'tcx>>, NoSolution>,
166 ) {
167if let Some(inspector) = infcx.obligation_inspector.get() {
168let result = match result {
169Ok(GoalEvaluation { certainty, .. }) => Ok(*certainty),
170Err(NoSolution) => Err(NoSolution),
171 };
172 (inspector)(infcx, &obligation, result);
173 }
174 }
175}
176177impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>
178where
179E: FromSolverError<'tcx, NextSolverError<'tcx>>,
180{
181#[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("register_predicate_obligation",
"rustc_trait_selection::solve::fulfill",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_trait_selection/src/solve/fulfill.rs"),
::tracing_core::__macro_support::Option::Some(181u32),
::tracing_core::__macro_support::Option::Some("rustc_trait_selection::solve::fulfill"),
::tracing_core::field::FieldSet::new(&["obligation"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&obligation)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: () = loop {};
return __tracing_attr_fake_return;
}
{
match (&self.usable_in_snapshot, &infcx.num_open_snapshots()) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
self.obligations.register(obligation, None);
}
}
}#[instrument(level = "trace", skip(self, infcx))]182fn register_predicate_obligation(
183&mut self,
184 infcx: &InferCtxt<'tcx>,
185 obligation: PredicateObligation<'tcx>,
186 ) {
187assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
188self.obligations.register(obligation, None);
189 }
190191fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
192self.obligations
193 .pending
194 .drain(..)
195 .map(|(obligation, _)| NextSolverError::Ambiguity(obligation))
196 .chain(
197self.obligations
198 .overflowed
199 .drain(..)
200 .map(|obligation| NextSolverError::Overflow(obligation)),
201 )
202 .map(|e| E::from_solver_error(infcx, e))
203 .collect()
204 }
205206fn try_evaluate_obligations(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
207match (&self.usable_in_snapshot, &infcx.num_open_snapshots()) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
208let mut errors = Vec::new();
209loop {
210let mut any_changed = false;
211for (mut obligation, stalled_on) in self.obligations.drain_pending(|_, _| true) {
212if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {
213self.obligations.on_fulfillment_overflow(infcx);
214// Only return true errors that we have accumulated while processing.
215return errors;
216 }
217218let goal = obligation.as_goal();
219let delegate = <&SolverDelegate<'tcx>>::from(infcx);
220if !delegate.disable_trait_solver_fast_paths()
221 && let Some(certainty) =
222 delegate.compute_goal_fast_path(goal, obligation.cause.span)
223 {
224match certainty {
225// This fast path doesn't depend on region identity so it doesn't
226 // matter if the goal contains inference variables or not, so we
227 // don't need to call `push_hir_typeck_potentially_region_dependent_goal`
228 // here.
229 //
230 // Only goals proven via the trait solver should be region dependent.
231Certainty::Yes => {}
232 Certainty::Maybe(_) => {
233self.obligations.register(obligation, None);
234 }
235 }
236continue;
237 }
238239let result = delegate.evaluate_root_goal(goal, obligation.cause.span, stalled_on);
240self.inspect_evaluated_obligation(infcx, &obligation, &result);
241let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result {
242Ok(result) => result,
243Err(NoSolution) => {
244 errors.push(E::from_solver_error(
245 infcx,
246 NextSolverError::TrueError(obligation),
247 ));
248continue;
249 }
250 };
251252// We've resolved the goal in `evaluate_root_goal`, avoid redoing this work
253 // in the next iteration. This does not resolve the inference variables
254 // constrained by evaluating the goal.
255obligation.predicate = goal.predicate;
256if has_changed == HasChanged::Yes {
257// We increment the recursion depth here to track the number of times
258 // this goal has resulted in inference progress. This doesn't precisely
259 // model the way that we track recursion depth in the old solver due
260 // to the fact that we only process root obligations, but it is a good
261 // approximation and should only result in fulfillment overflow in
262 // pathological cases.
263obligation.recursion_depth += 1;
264 any_changed = true;
265 }
266267match certainty {
268 Certainty::Yes => {
269// Goals may depend on structural identity. Region uniquification at the
270 // start of MIR borrowck may cause things to no longer be so, potentially
271 // causing an ICE.
272 //
273 // While we uniquify root goals in HIR this does not handle cases where
274 // regions are hidden inside of a type or const inference variable.
275 //
276 // FIXME(-Znext-solver): This does not handle inference variables hidden
277 // inside of an opaque type, e.g. if there's `Opaque = (?x, ?x)` in the
278 // storage, we can also rely on structural identity of `?x` even if we
279 // later uniquify it in MIR borrowck.
280if infcx.in_hir_typeck
281 && (obligation.has_non_region_infer() || obligation.has_free_regions())
282 {
283 infcx.push_hir_typeck_potentially_region_dependent_goal(obligation);
284 }
285 }
286 Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on),
287 }
288 }
289290if !any_changed {
291break;
292 }
293 }
294295errors296 }
297298fn has_pending_obligations(&self) -> bool {
299self.obligations.has_pending_obligations()
300 }
301302fn pending_obligations(&self) -> PredicateObligations<'tcx> {
303self.obligations.clone_pending()
304 }
305306fn pending_obligations_potentially_referencing_sub_root(
307&self,
308 infcx: &InferCtxt<'tcx>,
309 vid: ty::TyVid,
310 ) -> PredicateObligations<'tcx> {
311// `-Zdisable-fast-paths`: same gate as the other new-solver fast paths.
312if infcx.tcx.disable_trait_solver_fast_paths() {
313return self.obligations.clone_pending();
314 }
315self.obligations.clone_pending_potentially_referencing_sub_root(infcx, vid)
316 }
317318fn drain_stalled_obligations_for_coroutines(
319&mut self,
320 infcx: &InferCtxt<'tcx>,
321 ) -> PredicateObligations<'tcx> {
322let stalled_coroutines = match infcx.typing_mode_raw().assert_not_erased() {
323TypingMode::Analysis { defining_opaque_types_and_generators } => {
324defining_opaque_types_and_generators325 }
326TypingMode::Coherence327 | TypingMode::Borrowck { defining_opaque_types: _ }
328 | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ }
329 | TypingMode::PostAnalysis330 | TypingMode::Codegen => return Default::default(),
331 };
332333if stalled_coroutines.is_empty() {
334return Default::default();
335 }
336337self.obligations
338 .drain_pending(|_, stalled_on| {
339stalled_on.as_ref().is_some_and(|s| match s.stalled_certainty {
340 Certainty::Maybe(MaybeInfo {
341 cause: _,
342 opaque_types_jank: _,
343 stalled_on_coroutines: StalledOnCoroutines::Yes,
344 }) => true,
345 Certainty::Maybe(_) | Certainty::Yes => false,
346 })
347 })
348 .into_iter()
349 .map(|(o, _)| o)
350 .collect()
351 }
352}
353354pub enum NextSolverError<'tcx> {
355 TrueError(PredicateObligation<'tcx>),
356 Ambiguity(PredicateObligation<'tcx>),
357 Overflow(PredicateObligation<'tcx>),
358}
359360impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
361fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
362match error {
363 NextSolverError::TrueError(obligation) => {
364fulfillment_error_for_no_solution(infcx, obligation)
365 }
366 NextSolverError::Ambiguity(obligation) => {
367fulfillment_error_for_stalled(infcx, obligation)
368 }
369 NextSolverError::Overflow(obligation) => {
370fulfillment_error_for_overflow(infcx, obligation)
371 }
372 }
373 }
374}
375376impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
377fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
378match error {
379 NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
380 NextSolverError::Ambiguity(_) | NextSolverError::Overflow(_) => {
381 ScrubbedTraitError::Ambiguity382 }
383 }
384 }
385}