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::error_reporting::InferCtxtErrorExt;
21use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError};
2223mod derive_errors;
2425// `ThinVec` is important for performance, but not for the usual memory layout reasons.
26// `try_evaluate_obligations` is extremely hot and uses `retain_mut`. `ThinVec::retain_mut` is
27// simple and sub-optimal in terms of how it moves elements, but it can be inlined.
28// `Vec::retain_mut` is more sophisticated and minimizes element moves, but also contains more code
29// and doesn't get inlined in `try_evaluate_obligations`, giving worse performance overall.
30type PendingObligations<'tcx> =
31ThinVec<(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)>;
3233/// A trait engine using the new trait solver.
34///
35/// This is mostly identical to how `evaluate_all` works inside of the
36/// solver, except that the requirements are slightly different.
37///
38/// Unlike `evaluate_all` it is possible to add new obligations later on
39/// and we also have to track diagnostics information by using `Obligation`
40/// instead of `Goal`.
41///
42/// It is also likely that we want to use slightly different datastructures
43/// here as this will have to deal with far more root goals than `evaluate_all`.
44pub struct FulfillmentCtxt<'tcx, E: 'tcx> {
45 obligations: ObligationStorage<'tcx>,
4647/// The snapshot in which this context was created. Using the context
48 /// outside of this snapshot leads to subtle bugs if the snapshot
49 /// gets rolled back. Because of this we explicitly check that we only
50 /// use the context in exactly this snapshot.
51usable_in_snapshot: usize,
52 _errors: PhantomData<E>,
53}
5455#[derive(#[automatically_derived]
impl<'tcx> ::core::default::Default for ObligationStorage<'tcx> {
#[inline]
fn default() -> Self {
Self { 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_field1_finish(f,
"ObligationStorage", "pending", &&self.pending)
}
}Debug)]
56struct ObligationStorage<'tcx> {
57 pending: PendingObligations<'tcx>,
58}
5960impl<'tcx> ObligationStorage<'tcx> {
61fn register(
62&mut self,
63 obligation: PredicateObligation<'tcx>,
64 stalled_on: Option<GoalStalledOn<TyCtxt<'tcx>>>,
65 ) {
66self.pending.push((obligation, stalled_on));
67 }
6869fn has_pending_obligations(&self) -> bool {
70 !self.pending.is_empty()
71 }
7273fn clone_pending(&self) -> PredicateObligations<'tcx> {
74self.pending.iter().map(|(o, _)| o.clone()).collect()
75 }
7677fn clone_pending_filtered<F>(&self, f: F) -> PredicateObligations<'tcx>
78where
79F: FnMut(&&(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)) -> bool,
80 {
81self.pending.iter().filter(f).map(|(o, _)| o.clone()).collect()
82 }
8384fn drain_pending(
85&mut self,
86 cond: impl Fn(&PredicateObligation<'tcx>, &Option<GoalStalledOn<TyCtxt<'tcx>>>) -> bool,
87 ) -> PendingObligations<'tcx> {
88let (unstalled, pending) =
89 mem::take(&mut self.pending).into_iter().partition(|(o, s)| cond(o, s));
90self.pending = pending;
91unstalled92 }
93}
9495impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
96pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx, E> {
97if !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!(
98 infcx.next_trait_solver(),
99"new trait solver fulfillment context created when \
100 infcx is set up for old trait solver"
101);
102FulfillmentCtxt {
103 obligations: Default::default(),
104 usable_in_snapshot: infcx.num_open_snapshots(),
105 _errors: PhantomData,
106 }
107 }
108109fn inspect_evaluated_obligation(
110 infcx: &InferCtxt<'tcx>,
111 obligation: &PredicateObligation<'tcx>,
112 result: &Result<GoalEvaluation<TyCtxt<'tcx>>, NoSolution>,
113 ) {
114if let Some(inspector) = infcx.obligation_inspector.get() {
115let result = match result {
116Ok(GoalEvaluation { certainty, .. }) => Ok(*certainty),
117Err(NoSolution) => Err(NoSolution),
118 };
119 (inspector)(infcx, &obligation, result);
120 }
121 }
122}
123124impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>
125where
126E: FromSolverError<'tcx, NextSolverError<'tcx>>,
127{
128{}
#[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("/rustc-dev/d080e7dff1b0fc54541545252818f8cccf995d05/compiler/rustc_trait_selection/src/solve/fulfill.rs"),
::tracing_core::__macro_support::Option::Some(128u32),
::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))]129fn register_predicate_obligation(
130&mut self,
131 infcx: &InferCtxt<'tcx>,
132 obligation: PredicateObligation<'tcx>,
133 ) {
134assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
135136let delegate = <&SolverDelegate<'tcx>>::from(infcx);
137if let Some(GoalEvaluation { goal: _, certainty, has_changed: _, stalled_on }) =
138 compute_goal_fast_path(delegate, obligation.as_goal(), obligation.cause.span)
139 {
140// If we can take the fast path, don't even bother adding the goal to obligations,
141 // or if `Certainty::Maybe`, add it with precise stalled_on information.
142match certainty {
143 Certainty::Yes => {}
144 Certainty::Maybe(_) => {
145self.obligations.register(obligation, stalled_on);
146 }
147 }
148 } else {
149self.obligations.register(obligation, None);
150 }
151 }
152153#[inline]
154fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> TraitErrors<E> {
155if self.obligations.pending.is_empty() {
156// Typically in more than 99.9% of cases this condition is true, therefore we outline
157 // the other case.
158TraitErrors::NoErrors159 } else {
160let errors = collect_remaining_errors_impl(self, infcx);
161TraitErrors::from_iter(errors.into_iter())
162 }
163 }
164165fn try_evaluate_obligations(&mut self, infcx: &InferCtxt<'tcx>) -> TraitErrors<E> {
166{
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());
167let mut errors = TraitErrors::NoErrors;
168let delegate = <&SolverDelegate<'tcx>>::from(infcx);
169loop {
170let mut any_changed = false;
171172self.obligations.pending.retain_mut(|(obligation, opt_stalled_on)| {
173// Common case: still stalled; keep the obligation. This path is extremely hot in
174 // some cases; there can be thousands of pending obligations.
175if let Some(stalled_on) = opt_stalled_on176 && delegate.goal_remains_stalled(stalled_on)
177 {
178return true;
179 }
180181let result = delegate.evaluate_root_goal(
182obligation.as_goal(),
183obligation.cause.span,
184opt_stalled_on.take(),
185 );
186Self::inspect_evaluated_obligation(infcx, &obligation, &result);
187let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result {
188Ok(result) => result,
189Err(NoSolution) => {
190errors.push(E::from_solver_error(
191infcx,
192 NextSolverError::TrueError(obligation.clone()),
193 ));
194return false;
195 }
196 };
197198// We've resolved the goal in `evaluate_root_goal`, avoid redoing this work
199 // in the next iteration. This does not resolve the inference variables
200 // constrained by evaluating the goal.
201obligation.predicate = goal.predicate;
202if has_changed == HasChanged::Yes {
203if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {
204// We limit the total count of inference progress to avoid hang so we don't
205 // try to recover from this.
206 // It's more complicated to collect all overflows thus we stopped doing that.
207 // Eager aborting is also what the old solver does.
208 //
209 // Note: it's incredibly rare to actually encounter fulfillment overflow
210 // as a single obligation would have to result in different inference progress
211 // a `recursion_depth` number of times. This mostly happens in bugs or with
212 // `Subtype` obligations because we no longer use the `sub_unification_table`
213 // in generalization.
214infcx.err_ctxt().report_overflow_obligation(obligation, true);
215 } else {
216// We increment the recursion depth here to track the number of times
217 // this goal has resulted in inference progress. This doesn't precisely
218 // model the way that we track recursion depth in the old solver due
219 // to the fact that we only process root obligations, but it is a good
220 // approximation and should only result in fulfillment overflow in
221 // pathological cases.
222obligation.recursion_depth += 1;
223any_changed = true;
224 }
225 }
226227match certainty {
228 Certainty::Yes => {
229// Goals may depend on structural identity. Region uniquification at the
230 // start of MIR borrowck may cause things to no longer be so, potentially
231 // causing an ICE.
232 //
233 // While we uniquify root goals in HIR this does not handle cases where
234 // regions are hidden inside of a type or const inference variable.
235 //
236 // FIXME(-Znext-solver): This does not handle inference variables hidden
237 // inside of an opaque type, e.g. if there's `Opaque = (?x, ?x)` in the
238 // storage, we can also rely on structural identity of `?x` even if we
239 // later uniquify it in MIR borrowck.
240if infcx.in_hir_typeck
241 && (obligation.has_non_region_infer() || obligation.has_free_regions())
242 {
243infcx.push_hir_typeck_potentially_region_dependent_goal(
244obligation.clone(),
245 );
246 }
247false
248}
249 Certainty::Maybe(_) => {
250// Update `opt_stalled_on` goal, for the next retain_mut, because we are
251 // running until a fixpoint.
252*opt_stalled_on = stalled_on;
253true
254}
255 }
256 });
257258if !any_changed {
259break;
260 }
261 }
262263errors264 }
265266fn has_pending_obligations(&self) -> bool {
267self.obligations.has_pending_obligations()
268 }
269270fn pending_obligations(&self) -> PredicateObligations<'tcx> {
271self.obligations.clone_pending()
272 }
273274fn pending_obligations_potentially_referencing_sub_root(
275&self,
276 infcx: &InferCtxt<'tcx>,
277 vid: ty::TyVid,
278 ) -> PredicateObligations<'tcx> {
279// `-Zdisable-fast-paths`: same gate as the other new-solver fast paths.
280if infcx.tcx.disable_trait_solver_fast_paths() {
281return self.obligations.clone_pending();
282 }
283self.obligations.clone_pending_filtered(|(_, stalled_on)| {
284let Some(stalled_on) = stalled_onelse { return true };
285// Don't reuse the sub-unification roots cached on `stalled_on`:
286 // a later sub-unification merge can have changed which root
287 // each stalled var belongs to, so the cached info can be stale.
288 // Walk `stalled_vars` and recompute the current root instead.
289 //
290 // Conservative here: if a stalled var no longer resolves to an
291 // infer var, some unification happened, so the goal is no longer
292 // stalled. Include it to be re-evaluated downstream.
293stalled_on.stalled_vars.iter().filter_map(|arg| arg.as_type(infcx.tcx)).any(|ty| {
294match *infcx.shallow_resolve(ty).kind() {
295 ty::Infer(ty::TyVar(tv)) => infcx.sub_unification_table_root_var(tv) == vid,
296_ => true,
297 }
298 })
299 })
300 }
301302fn pending_obligations_potentially_referencing_float_infer(
303&self,
304 infcx: &InferCtxt<'tcx>,
305 ) -> PredicateObligations<'tcx> {
306// `-Zdisable-fast-paths`: same gate as the other new-solver fast paths.
307if infcx.tcx.disable_trait_solver_fast_paths() {
308return self.obligations.clone_pending();
309 }
310311self.obligations.clone_pending_filtered(|(_, stalled_on)| {
312let Some(stalled_on) = stalled_onelse { return true };
313// If the stalled vars don't have float infers, the nested goals won't
314 // have them either. We only create float infers for user written literals.
315stalled_on316 .stalled_vars
317 .iter()
318 .filter_map(|arg| arg.as_type(infcx.tcx))
319 .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(_))))
320 })
321 }
322323fn drain_stalled_obligations_for_coroutines(
324&mut self,
325 infcx: &InferCtxt<'tcx>,
326 ) -> PredicateObligations<'tcx> {
327let stalled_coroutines = match infcx.typing_mode_raw().assert_not_erased() {
328TypingMode::Typeck { defining_opaque_types_and_generators } => {
329defining_opaque_types_and_generators330 }
331TypingMode::Coherence332 | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: _ }
333 | TypingMode::PostBorrowck { defined_opaque_types: _ }
334 | TypingMode::Reflection335 | TypingMode::PostAnalysis336 | TypingMode::Codegen => return Default::default(),
337 };
338339if stalled_coroutines.is_empty() {
340return Default::default();
341 }
342343self.obligations
344 .drain_pending(|_, stalled_on| {
345stalled_on.as_ref().is_some_and(|s| {
346match s.stalled_maybe_info.stalled_on_coroutines {
347 StalledOnCoroutines::Yes => true,
348 StalledOnCoroutines::No => false,
349 }
350 })
351 })
352 .into_iter()
353 .map(|(o, _)| o)
354 .collect()
355 }
356}
357358#[cold]
359#[inline(never)]
360fn collect_remaining_errors_impl<'tcx, E>(
361 cx: &mut FulfillmentCtxt<'tcx, E>,
362 infcx: &InferCtxt<'tcx>,
363) -> ThinVec<E>
364where
365E: FromSolverError<'tcx, NextSolverError<'tcx>>,
366{
367cx.obligations
368 .pending
369 .drain(..)
370 .filter_map(|(obligation, _)| {
371try_ambiguity_error_for_stalled(infcx, obligation).map(NextSolverError::Ambiguity)
372 })
373 .map(|e| E::from_solver_error(infcx, e))
374 .collect()
375}
376377// We evaluate stalled obligations while collecting remaining errors because a
378// previously ambiguous goal may have become successful. In that case we emit a
379// delayed bug instead of producing a fulfillment error. Store the diagnostic
380// information here so error conversion does not reevaluate the goal.
381pub struct NextSolverAmbiguityError<'tcx> {
382 root_obligation: PredicateObligation<'tcx>,
383 code: FulfillmentErrorCode<'tcx>,
384 refine_obligation: bool,
385}
386387pub enum NextSolverError<'tcx> {
388 TrueError(PredicateObligation<'tcx>),
389 Ambiguity(NextSolverAmbiguityError<'tcx>),
390}
391392impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
393fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
394match error {
395 NextSolverError::TrueError(obligation) => {
396fulfillment_error_for_no_solution(infcx, obligation)
397 }
398 NextSolverError::Ambiguity(ambiguity) => {
399fulfillment_error_for_stalled(infcx, ambiguity)
400 }
401 }
402 }
403}
404405impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
406fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
407match error {
408 NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
409 NextSolverError::Ambiguity(_) => ScrubbedTraitError::Ambiguity,
410 }
411 }
412}
413414// Some types are used a lot. Make sure they don't unintentionally get bigger.
415#[cfg(target_pointer_width = "64")]
416mod size_asserts {
417use rustc_data_structures::static_assert_size;
418419use super::*;
420// tidy-alphabetical-start
421 // Before #160005 this pair was greater than 128 bytes, which triggered the use of (slow)
422 // `memcpy` for moving elements of `PendingObligations`. Then #160479 greatly reduced the
423 // number of `memcpy` operations in `try_evaluate_obligations`. So the size of this pair is
424 // much less important than it was, but still shouldn't be changed without some thought.
425const _: [(); 104] =
[();
::std::mem::size_of::<(PredicateObligation<'_>,
Option<GoalStalledOn<TyCtxt<'_>>>)>()];static_assert_size!((PredicateObligation<'_>, Option<GoalStalledOn<TyCtxt<'_>>>), 104);
426// tidy-alphabetical-end
427}