rustc_trait_selection/solve/
fulfill.rs1use std::marker::PhantomData;
2use std::mem;
3
4use rustc_infer::infer::InferCtxt;
5use rustc_infer::traits::query::NoSolution;
6use rustc_infer::traits::{
7 FromSolverError, 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::{
12 GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt as _, StalledOnCoroutines,
13};
14use thin_vec::ThinVec;
15use tracing::instrument;
16
17use self::derive_errors::*;
18use super::Certainty;
19use super::delegate::SolverDelegate;
20use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError};
21
22mod derive_errors;
23
24type PendingObligations<'tcx> =
30 ThinVec<(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)>;
31
32pub struct FulfillmentCtxt<'tcx, E: 'tcx> {
44 obligations: ObligationStorage<'tcx>,
45
46 usable_in_snapshot: usize,
51 _errors: PhantomData<E>,
52}
53
54#[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)]
55struct ObligationStorage<'tcx> {
56 overflowed: Vec<PredicateObligation<'tcx>>,
62 pending: PendingObligations<'tcx>,
63}
64
65impl<'tcx> ObligationStorage<'tcx> {
66 fn register(
67 &mut self,
68 obligation: PredicateObligation<'tcx>,
69 stalled_on: Option<GoalStalledOn<TyCtxt<'tcx>>>,
70 ) {
71 self.pending.push((obligation, stalled_on));
72 }
73
74 fn has_pending_obligations(&self) -> bool {
75 !self.pending.is_empty() || !self.overflowed.is_empty()
76 }
77
78 fn clone_pending(&self) -> PredicateObligations<'tcx> {
79 let mut obligations: PredicateObligations<'tcx> =
80 self.pending.iter().map(|(o, _)| o.clone()).collect();
81 obligations.extend(self.overflowed.iter().cloned());
82 obligations
83 }
84
85 fn clone_pending_filtered<F>(&self, f: F) -> PredicateObligations<'tcx>
86 where
87 F: FnMut(&&(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)) -> bool,
88 {
89 let mut obligations: PredicateObligations<'tcx> =
90 self.pending.iter().filter(f).map(|(o, _)| o.clone()).collect();
91 obligations.extend(self.overflowed.iter().cloned());
92 obligations
93 }
94
95 fn drain_pending(
96 &mut self,
97 cond: impl Fn(&PredicateObligation<'tcx>, &Option<GoalStalledOn<TyCtxt<'tcx>>>) -> bool,
98 ) -> PendingObligations<'tcx> {
99 let (unstalled, pending) =
100 mem::take(&mut self.pending).into_iter().partition(|(o, s)| cond(o, s));
101 self.pending = pending;
102 unstalled
103 }
104
105 fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {
106 infcx.probe(|_| {
107 self.overflowed.extend(
113 self.pending
114 .extract_if(.., |(o, stalled_on)| {
115 let goal = o.as_goal();
116 let result = <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal(
117 goal,
118 o.cause.span,
119 stalled_on.take(),
120 );
121 #[allow(non_exhaustive_omitted_patterns)] match result {
Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }) => true,
_ => false,
}matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }))
122 })
123 .map(|(o, _)| o),
124 );
125 })
126 }
127}
128
129impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
130 pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx, E> {
131 if !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!(
132 infcx.next_trait_solver(),
133 "new trait solver fulfillment context created when \
134 infcx is set up for old trait solver"
135 );
136 FulfillmentCtxt {
137 obligations: Default::default(),
138 usable_in_snapshot: infcx.num_open_snapshots(),
139 _errors: PhantomData,
140 }
141 }
142
143 fn inspect_evaluated_obligation(
144 infcx: &InferCtxt<'tcx>,
145 obligation: &PredicateObligation<'tcx>,
146 result: &Result<GoalEvaluation<TyCtxt<'tcx>>, NoSolution>,
147 ) {
148 if let Some(inspector) = infcx.obligation_inspector.get() {
149 let result = match result {
150 Ok(GoalEvaluation { certainty, .. }) => Ok(*certainty),
151 Err(NoSolution) => Err(NoSolution),
152 };
153 (inspector)(infcx, &obligation, result);
154 }
155 }
156}
157
158impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>
159where
160 E: FromSolverError<'tcx, NextSolverError<'tcx>>,
161{
162 {}
#[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/cea272fa356e94bd2ee2cadf376630aa0683867a/compiler/rustc_trait_selection/src/solve/fulfill.rs"),
::tracing_core::__macro_support::Option::Some(162u32),
::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))]
163 fn register_predicate_obligation(
164 &mut self,
165 infcx: &InferCtxt<'tcx>,
166 obligation: PredicateObligation<'tcx>,
167 ) {
168 assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
169
170 let delegate = <&SolverDelegate<'tcx>>::from(infcx);
171 if let Some(GoalEvaluation { goal: _, certainty, has_changed: _, stalled_on }) =
172 compute_goal_fast_path(delegate, obligation.as_goal(), obligation.cause.span)
173 {
174 match certainty {
177 Certainty::Yes => {}
178 Certainty::Maybe(_) => {
179 self.obligations.register(obligation, stalled_on);
180 }
181 }
182 } else {
183 self.obligations.register(obligation, None);
184 }
185 }
186
187 #[inline]
188 fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> TraitErrors<E> {
189 if self.obligations.pending.is_empty() && self.obligations.overflowed.is_empty() {
190 TraitErrors::NoErrors
193 } else {
194 let errors = collect_remaining_errors_impl(self, infcx);
195 TraitErrors::from_iter(errors.into_iter())
196 }
197 }
198
199 fn try_evaluate_obligations(&mut self, infcx: &InferCtxt<'tcx>) -> TraitErrors<E> {
200 {
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());
201 let mut errors = TraitErrors::NoErrors;
202 let delegate = <&SolverDelegate<'tcx>>::from(infcx);
203 loop {
204 let mut any_changed = false;
205 let mut overflowed = false;
206
207 self.obligations.pending.retain_mut(|(obligation, opt_stalled_on)| {
208 if overflowed {
209 return false;
210 }
211
212 if let Some(stalled_on) = opt_stalled_on
215 && delegate.goal_remains_stalled(stalled_on)
216 {
217 return true;
218 }
219
220 let result = delegate.evaluate_root_goal(
221 obligation.as_goal(),
222 obligation.cause.span,
223 opt_stalled_on.take(),
224 );
225 Self::inspect_evaluated_obligation(infcx, &obligation, &result);
226 let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result {
227 Ok(result) => result,
228 Err(NoSolution) => {
229 errors.push(E::from_solver_error(
230 infcx,
231 NextSolverError::TrueError(obligation.clone()),
232 ));
233 return false;
234 }
235 };
236
237 obligation.predicate = goal.predicate;
241 if has_changed == HasChanged::Yes {
242 obligation.recursion_depth += 1;
249
250 if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {
251 overflowed = true;
255 return false;
256 } else {
257 any_changed = true;
258 }
259 }
260
261 match certainty {
262 Certainty::Yes => {
263 if infcx.in_hir_typeck
275 && (obligation.has_non_region_infer() || obligation.has_free_regions())
276 {
277 infcx.push_hir_typeck_potentially_region_dependent_goal(
278 obligation.clone(),
279 );
280 }
281 false
282 }
283 Certainty::Maybe(_) => {
284 *opt_stalled_on = stalled_on;
287 true
288 }
289 }
290 });
291 if overflowed {
292 self.obligations.on_fulfillment_overflow(infcx);
293 return errors;
295 }
296
297 if !any_changed {
298 break;
299 }
300 }
301
302 errors
303 }
304
305 fn has_pending_obligations(&self) -> bool {
306 self.obligations.has_pending_obligations()
307 }
308
309 fn pending_obligations(&self) -> PredicateObligations<'tcx> {
310 self.obligations.clone_pending()
311 }
312
313 fn pending_obligations_potentially_referencing_sub_root(
314 &self,
315 infcx: &InferCtxt<'tcx>,
316 vid: ty::TyVid,
317 ) -> PredicateObligations<'tcx> {
318 if infcx.tcx.disable_trait_solver_fast_paths() {
320 return self.obligations.clone_pending();
321 }
322 self.obligations.clone_pending_filtered(|(_, stalled_on)| {
323 let Some(stalled_on) = stalled_on else { return true };
324 stalled_on.stalled_vars.iter().filter_map(|arg| arg.as_type(infcx.tcx)).any(|ty| {
333 match *infcx.shallow_resolve(ty).kind() {
334 ty::Infer(ty::TyVar(tv)) => infcx.sub_unification_table_root_var(tv) == vid,
335 _ => true,
336 }
337 })
338 })
339 }
340
341 fn pending_obligations_potentially_referencing_float_infer(
342 &self,
343 infcx: &InferCtxt<'tcx>,
344 ) -> PredicateObligations<'tcx> {
345 if infcx.tcx.disable_trait_solver_fast_paths() {
347 return self.obligations.clone_pending();
348 }
349
350 self.obligations.clone_pending_filtered(|(_, stalled_on)| {
351 let Some(stalled_on) = stalled_on else { return true };
352 stalled_on
355 .stalled_vars
356 .iter()
357 .filter_map(|arg| arg.as_type(infcx.tcx))
358 .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(_))))
359 })
360 }
361
362 fn drain_stalled_obligations_for_coroutines(
363 &mut self,
364 infcx: &InferCtxt<'tcx>,
365 ) -> PredicateObligations<'tcx> {
366 let stalled_coroutines = match infcx.typing_mode_raw().assert_not_erased() {
367 TypingMode::Typeck { defining_opaque_types_and_generators } => {
368 defining_opaque_types_and_generators
369 }
370 TypingMode::Coherence
371 | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: _ }
372 | TypingMode::PostBorrowck { defined_opaque_types: _ }
373 | TypingMode::Reflection
374 | TypingMode::PostAnalysis
375 | TypingMode::Codegen => return Default::default(),
376 };
377
378 if stalled_coroutines.is_empty() {
379 return Default::default();
380 }
381
382 self.obligations
383 .drain_pending(|_, stalled_on| {
384 stalled_on.as_ref().is_some_and(|s| {
385 match s.stalled_maybe_info.stalled_on_coroutines {
386 StalledOnCoroutines::Yes => true,
387 StalledOnCoroutines::No => false,
388 }
389 })
390 })
391 .into_iter()
392 .map(|(o, _)| o)
393 .collect()
394 }
395}
396
397#[cold]
398#[inline(never)]
399fn collect_remaining_errors_impl<'tcx, E>(
400 cx: &mut FulfillmentCtxt<'tcx, E>,
401 infcx: &InferCtxt<'tcx>,
402) -> ThinVec<E>
403where
404 E: FromSolverError<'tcx, NextSolverError<'tcx>>,
405{
406 cx.obligations
407 .pending
408 .drain(..)
409 .filter_map(|(obligation, _)| {
410 try_ambiguity_error_for_stalled(infcx, obligation).map(NextSolverError::Ambiguity)
411 })
412 .chain(
413 cx.obligations
414 .overflowed
415 .drain(..)
416 .map(|obligation| NextSolverError::Overflow(obligation)),
417 )
418 .map(|e| E::from_solver_error(infcx, e))
419 .collect()
420}
421
422pub struct NextSolverAmbiguityError<'tcx> {
427 root_obligation: PredicateObligation<'tcx>,
428 code: FulfillmentErrorCode<'tcx>,
429 refine_obligation: bool,
430}
431
432pub enum NextSolverError<'tcx> {
433 TrueError(PredicateObligation<'tcx>),
434 Ambiguity(NextSolverAmbiguityError<'tcx>),
435 Overflow(PredicateObligation<'tcx>),
436}
437
438impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
439 fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
440 match error {
441 NextSolverError::TrueError(obligation) => {
442 fulfillment_error_for_no_solution(infcx, obligation)
443 }
444 NextSolverError::Ambiguity(ambiguity) => {
445 fulfillment_error_for_stalled(infcx, ambiguity)
446 }
447 NextSolverError::Overflow(obligation) => {
448 fulfillment_error_for_overflow(infcx, obligation)
449 }
450 }
451 }
452}
453
454impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
455 fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
456 match error {
457 NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
458 NextSolverError::Ambiguity(_) | NextSolverError::Overflow(_) => {
459 ScrubbedTraitError::Ambiguity
460 }
461 }
462 }
463}
464
465#[cfg(target_pointer_width = "64")]
467mod size_asserts {
468 use rustc_data_structures::static_assert_size;
469
470 use super::*;
471 const _: [(); 104] =
[();
::std::mem::size_of::<(PredicateObligation<'_>,
Option<GoalStalledOn<TyCtxt<'_>>>)>()];static_assert_size!((PredicateObligation<'_>, Option<GoalStalledOn<TyCtxt<'_>>>), 104);
477 }