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, TraitErrors,
8};
9use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, TypingMode};
10use rustc_next_trait_solver::solve::fast_path::compute_goal_fast_path;
11use rustc_next_trait_solver::solve::{
12GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExtas _, StalledOnCoroutines,
13};
14use thin_vec::ThinVec;
15use tracing::instrument;
1617use self::derive_errors::*;
18use super::Certainty;
19use super::delegate::SolverDelegate;
20use crate::traits::{FulfillmentError, ScrubbedTraitError};
2122mod derive_errors;
2324// FIXME: Do we need to use a `ThinVec` here?
25type PendingObligations<'tcx> =
26ThinVec<(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)>;
2728/// A trait engine using the new trait solver.
29///
30/// This is mostly identical to how `evaluate_all` works inside of the
31/// solver, except that the requirements are slightly different.
32///
33/// Unlike `evaluate_all` it is possible to add new obligations later on
34/// and we also have to track diagnostics information by using `Obligation`
35/// instead of `Goal`.
36///
37/// It is also likely that we want to use slightly different datastructures
38/// here as this will have to deal with far more root goals than `evaluate_all`.
39pub struct FulfillmentCtxt<'tcx, E: 'tcx> {
40 obligations: ObligationStorage<'tcx>,
4142/// The snapshot in which this context was created. Using the context
43 /// outside of this snapshot leads to subtle bugs if the snapshot
44 /// gets rolled back. Because of this we explicitly check that we only
45 /// use the context in exactly this snapshot.
46usable_in_snapshot: usize,
47 _errors: PhantomData<E>,
48}
4950#[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)]
51struct ObligationStorage<'tcx> {
52/// Obligations which resulted in an overflow in fulfillment itself.
53 ///
54 /// We cannot eagerly return these as error so we instead store them here
55 /// to avoid recomputing them each time `try_evaluate_obligations` is called.
56 /// This also allows us to return the correct `FulfillmentError` for them.
57overflowed: Vec<PredicateObligation<'tcx>>,
58 pending: PendingObligations<'tcx>,
59}
6061impl<'tcx> ObligationStorage<'tcx> {
62fn register(
63&mut self,
64 obligation: PredicateObligation<'tcx>,
65 stalled_on: Option<GoalStalledOn<TyCtxt<'tcx>>>,
66 ) {
67self.pending.push((obligation, stalled_on));
68 }
6970fn has_pending_obligations(&self) -> bool {
71 !self.pending.is_empty() || !self.overflowed.is_empty()
72 }
7374fn clone_pending(&self) -> PredicateObligations<'tcx> {
75let mut obligations: PredicateObligations<'tcx> =
76self.pending.iter().map(|(o, _)| o.clone()).collect();
77obligations.extend(self.overflowed.iter().cloned());
78obligations79 }
8081fn clone_pending_filtered<F>(&self, f: F) -> PredicateObligations<'tcx>
82where
83F: FnMut(&&(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)) -> bool,
84 {
85let mut obligations: PredicateObligations<'tcx> =
86self.pending.iter().filter(f).map(|(o, _)| o.clone()).collect();
87obligations.extend(self.overflowed.iter().cloned());
88obligations89 }
9091fn drain_pending(
92&mut self,
93 cond: impl Fn(&PredicateObligation<'tcx>, &Option<GoalStalledOn<TyCtxt<'tcx>>>) -> bool,
94 ) -> PendingObligations<'tcx> {
95let (unstalled, pending) =
96 mem::take(&mut self.pending).into_iter().partition(|(o, s)| cond(o, s));
97self.pending = pending;
98unstalled99 }
100101fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {
102infcx.probe(|_| {
103// IMPORTANT: we must not use solve any inference variables in the obligations
104 // as this is all happening inside of a probe. We use a probe to make sure
105 // we get all obligations involved in the overflow. We pretty much check: if
106 // we were to do another step of `try_evaluate_obligations`, which goals would
107 // change.
108self.overflowed.extend(
109self.pending
110 .extract_if(.., |(o, stalled_on)| {
111let goal = o.as_goal();
112let result = <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal(
113goal,
114o.cause.span,
115stalled_on.take(),
116 );
117#[allow(non_exhaustive_omitted_patterns)] match result {
Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }) => true,
_ => false,
}matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }))118 })
119 .map(|(o, _)| o),
120 );
121 })
122 }
123}
124125impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
126pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx, E> {
127if !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!(
128 infcx.next_trait_solver(),
129"new trait solver fulfillment context created when \
130 infcx is set up for old trait solver"
131);
132FulfillmentCtxt {
133 obligations: Default::default(),
134 usable_in_snapshot: infcx.num_open_snapshots(),
135 _errors: PhantomData,
136 }
137 }
138139fn inspect_evaluated_obligation(
140 infcx: &InferCtxt<'tcx>,
141 obligation: &PredicateObligation<'tcx>,
142 result: &Result<GoalEvaluation<TyCtxt<'tcx>>, NoSolution>,
143 ) {
144if let Some(inspector) = infcx.obligation_inspector.get() {
145let result = match result {
146Ok(GoalEvaluation { certainty, .. }) => Ok(*certainty),
147Err(NoSolution) => Err(NoSolution),
148 };
149 (inspector)(infcx, &obligation, result);
150 }
151 }
152}
153154impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>
155where
156E: FromSolverError<'tcx, NextSolverError<'tcx>>,
157{
158#[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(158u32),
::tracing_core::__macro_support::Option::Some("rustc_trait_selection::solve::fulfill"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("obligation")
}> =
::tracing::__macro_support::FieldName::new("obligation");
NAME.as_str()
}], ::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};
meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&obligation)
as &dyn ::tracing::field::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);
}
}
}
};
let delegate = <&SolverDelegate<'tcx>>::from(infcx);
if let Some(GoalEvaluation {
goal: _, certainty, has_changed: _, stalled_on }) =
compute_goal_fast_path(delegate, obligation.as_goal(),
obligation.cause.span) {
match certainty {
Certainty::Yes => {}
Certainty::Maybe(_) => {
self.obligations.register(obligation, stalled_on);
}
}
} else { self.obligations.register(obligation, None); }
}
}
}#[instrument(level = "trace", skip(self, infcx))]159fn register_predicate_obligation(
160&mut self,
161 infcx: &InferCtxt<'tcx>,
162 obligation: PredicateObligation<'tcx>,
163 ) {
164assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
165166let delegate = <&SolverDelegate<'tcx>>::from(infcx);
167if let Some(GoalEvaluation { goal: _, certainty, has_changed: _, stalled_on }) =
168 compute_goal_fast_path(delegate, obligation.as_goal(), obligation.cause.span)
169 {
170// If we can take the fast path, don't even bother adding the goal to obligations,
171 // or if `Certainty::Maybe`, add it with precise stalled_on information.
172match certainty {
173 Certainty::Yes => {}
174 Certainty::Maybe(_) => {
175self.obligations.register(obligation, stalled_on);
176 }
177 }
178 } else {
179self.obligations.register(obligation, None);
180 }
181 }
182183#[inline]
184fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> TraitErrors<E> {
185if self.obligations.pending.is_empty() && self.obligations.overflowed.is_empty() {
186// Typically in more than 99.9% of cases this condition is true, therefore we outline
187 // the other case.
188TraitErrors::NoErrors189 } else {
190 TraitErrors::HasErrors(collect_remaining_errors_impl(self, infcx))
191 }
192 }
193194fn try_evaluate_obligations(&mut self, infcx: &InferCtxt<'tcx>) -> TraitErrors<E> {
195{
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);
}
}
}
};assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
196let mut errors = TraitErrors::NoErrors;
197let delegate = <&SolverDelegate<'tcx>>::from(infcx);
198loop {
199let mut any_changed = false;
200let mut overflowed = false;
201202self.obligations.pending.retain_mut(|(obligation, opt_stalled_on)| {
203if overflowed {
204return false;
205 }
206207// Common case: still stalled; keep the obligation. This path is extremely hot in
208 // some cases; there can be thousands of pending obligations.
209if let Some(stalled_on) = opt_stalled_on210 && delegate.goal_remains_stalled(stalled_on)
211 {
212return true;
213 }
214215let result = delegate.evaluate_root_goal(
216obligation.as_goal(),
217obligation.cause.span,
218opt_stalled_on.take(),
219 );
220Self::inspect_evaluated_obligation(infcx, &obligation, &result);
221let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result {
222Ok(result) => result,
223Err(NoSolution) => {
224errors.push(E::from_solver_error(
225infcx,
226 NextSolverError::TrueError(obligation.clone()),
227 ));
228return false;
229 }
230 };
231232// We've resolved the goal in `evaluate_root_goal`, avoid redoing this work
233 // in the next iteration. This does not resolve the inference variables
234 // constrained by evaluating the goal.
235obligation.predicate = goal.predicate;
236if has_changed == HasChanged::Yes {
237// We increment the recursion depth here to track the number of times
238 // this goal has resulted in inference progress. This doesn't precisely
239 // model the way that we track recursion depth in the old solver due
240 // to the fact that we only process root obligations, but it is a good
241 // approximation and should only result in fulfillment overflow in
242 // pathological cases.
243obligation.recursion_depth += 1;
244245if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {
246// At this point we want to stop evaluating goals. We can't break out of
247 // `retain_mut`, so instead we set this flag which causes all other
248 // elements to be skipped.
249overflowed = true;
250return false;
251 } else {
252any_changed = true;
253 }
254 }
255256match certainty {
257 Certainty::Yes => {
258// Goals may depend on structural identity. Region uniquification at the
259 // start of MIR borrowck may cause things to no longer be so, potentially
260 // causing an ICE.
261 //
262 // While we uniquify root goals in HIR this does not handle cases where
263 // regions are hidden inside of a type or const inference variable.
264 //
265 // FIXME(-Znext-solver): This does not handle inference variables hidden
266 // inside of an opaque type, e.g. if there's `Opaque = (?x, ?x)` in the
267 // storage, we can also rely on structural identity of `?x` even if we
268 // later uniquify it in MIR borrowck.
269if infcx.in_hir_typeck
270 && (obligation.has_non_region_infer() || obligation.has_free_regions())
271 {
272infcx.push_hir_typeck_potentially_region_dependent_goal(
273obligation.clone(),
274 );
275 }
276false
277}
278 Certainty::Maybe(_) => {
279// Update `opt_stalled_on` goal, for the next retain_mut, because we are
280 // running until a fixpoint.
281*opt_stalled_on = stalled_on;
282true
283}
284 }
285 });
286if overflowed {
287self.obligations.on_fulfillment_overflow(infcx);
288// Only return true errors that we have accumulated while processing.
289return errors;
290 }
291292if !any_changed {
293break;
294 }
295 }
296297errors298 }
299300fn has_pending_obligations(&self) -> bool {
301self.obligations.has_pending_obligations()
302 }
303304fn pending_obligations(&self) -> PredicateObligations<'tcx> {
305self.obligations.clone_pending()
306 }
307308fn pending_obligations_potentially_referencing_sub_root(
309&self,
310 infcx: &InferCtxt<'tcx>,
311 vid: ty::TyVid,
312 ) -> PredicateObligations<'tcx> {
313// `-Zdisable-fast-paths`: same gate as the other new-solver fast paths.
314if infcx.tcx.disable_trait_solver_fast_paths() {
315return self.obligations.clone_pending();
316 }
317self.obligations.clone_pending_filtered(|(_, stalled_on)| {
318let Some(stalled_on) = stalled_onelse { return true };
319// Don't reuse the sub-unification roots cached on `stalled_on`:
320 // a later sub-unification merge can have changed which root
321 // each stalled var belongs to, so the cached info can be stale.
322 // Walk `stalled_vars` and recompute the current root instead.
323 //
324 // Conservative here: if a stalled var no longer resolves to an
325 // infer var, some unification happened, so the goal is no longer
326 // stalled. Include it to be re-evaluated downstream.
327stalled_on.stalled_vars.iter().filter_map(|arg| arg.as_type(infcx.tcx)).any(|ty| {
328match *infcx.shallow_resolve(ty).kind() {
329 ty::Infer(ty::TyVar(tv)) => infcx.sub_unification_table_root_var(tv) == vid,
330_ => true,
331 }
332 })
333 })
334 }
335336fn pending_obligations_potentially_referencing_float_infer(
337&self,
338 infcx: &InferCtxt<'tcx>,
339 ) -> PredicateObligations<'tcx> {
340// `-Zdisable-fast-paths`: same gate as the other new-solver fast paths.
341if infcx.tcx.disable_trait_solver_fast_paths() {
342return self.obligations.clone_pending();
343 }
344345self.obligations.clone_pending_filtered(|(_, stalled_on)| {
346let Some(stalled_on) = stalled_onelse { return true };
347// If the stalled vars don't have float infers, the nested goals won't
348 // have them either. We only create float infers for user written literals.
349stalled_on350 .stalled_vars
351 .iter()
352 .filter_map(|arg| arg.as_type(infcx.tcx))
353 .any(|ty| #[allow(non_exhaustive_omitted_patterns)] match infcx.shallow_resolve(ty).kind()
{
ty::Infer(ty::FloatVar(_)) => true,
_ => false,
}matches!(infcx.shallow_resolve(ty).kind(), ty::Infer(ty::FloatVar(_))))
354 })
355 }
356357fn drain_stalled_obligations_for_coroutines(
358&mut self,
359 infcx: &InferCtxt<'tcx>,
360 ) -> PredicateObligations<'tcx> {
361let stalled_coroutines = match infcx.typing_mode_raw().assert_not_erased() {
362 TypingMode::Typeck { defining_opaque_types_and_generators } => {
363defining_opaque_types_and_generators364 }
365 TypingMode::Coherence366 | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: _ }
367 | TypingMode::PostBorrowck { defined_opaque_types: _ }
368 | TypingMode::Reflection369 | TypingMode::PostAnalysis370 | TypingMode::Codegen => return Default::default(),
371 };
372373if stalled_coroutines.is_empty() {
374return Default::default();
375 }
376377self.obligations
378 .drain_pending(|_, stalled_on| {
379stalled_on.as_ref().is_some_and(|s| {
380match s.stalled_maybe_info.stalled_on_coroutines {
381 StalledOnCoroutines::Yes => true,
382 StalledOnCoroutines::No => false,
383 }
384 })
385 })
386 .into_iter()
387 .map(|(o, _)| o)
388 .collect()
389 }
390}
391392#[cold]
393#[inline(never)]
394fn collect_remaining_errors_impl<'tcx, E>(
395 cx: &mut FulfillmentCtxt<'tcx, E>,
396 infcx: &InferCtxt<'tcx>,
397) -> ThinVec<E>
398where
399E: FromSolverError<'tcx, NextSolverError<'tcx>>,
400{
401cx.obligations
402 .pending
403 .drain(..)
404 .map(|(obligation, _)| NextSolverError::Ambiguity(obligation))
405 .chain(
406cx.obligations
407 .overflowed
408 .drain(..)
409 .map(|obligation| NextSolverError::Overflow(obligation)),
410 )
411 .map(|e| E::from_solver_error(infcx, e))
412 .collect()
413}
414415pub enum NextSolverError<'tcx> {
416 TrueError(PredicateObligation<'tcx>),
417 Ambiguity(PredicateObligation<'tcx>),
418 Overflow(PredicateObligation<'tcx>),
419}
420421impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
422fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
423match error {
424 NextSolverError::TrueError(obligation) => {
425fulfillment_error_for_no_solution(infcx, obligation)
426 }
427 NextSolverError::Ambiguity(obligation) => {
428fulfillment_error_for_stalled(infcx, obligation)
429 }
430 NextSolverError::Overflow(obligation) => {
431fulfillment_error_for_overflow(infcx, obligation)
432 }
433 }
434 }
435}
436437impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
438fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
439match error {
440 NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
441 NextSolverError::Ambiguity(_) | NextSolverError::Overflow(_) => {
442 ScrubbedTraitError::Ambiguity443 }
444 }
445 }
446}
447448// Some types are used a lot. Make sure they don't unintentionally get bigger.
449#[cfg(target_pointer_width = "64")]
450mod size_asserts {
451use rustc_data_structures::static_assert_size;
452453use super::*;
454// tidy-alphabetical-start
455 // Before #160005 this pair was greater than 128 bytes, which triggered the use of (slow)
456 // `memcpy` for moving elements of `PendingObligations`.
457const _: [(); 104] =
[();
::std::mem::size_of::<(PredicateObligation<'_>,
Option<GoalStalledOn<TyCtxt<'_>>>)>()];static_assert_size!((PredicateObligation<'_>, Option<GoalStalledOn<TyCtxt<'_>>>), 104);
458// tidy-alphabetical-end
459}