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::error_reporting::InferCtxtErrorExt;
21use crate::traits::{FulfillmentError, ScrubbedTraitError};
22
23mod derive_errors;
24
25type PendingObligations<'tcx> =
31 ThinVec<(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)>;
32
33pub struct FulfillmentCtxt<'tcx, E: 'tcx> {
45 obligations: ObligationStorage<'tcx>,
46
47 usable_in_snapshot: usize,
52 _errors: PhantomData<E>,
53}
54
55#[derive(#[automatically_derived]
impl<'tcx> ::core::default::Default for ObligationStorage<'tcx> {
#[inline]
fn default() -> Self {
ObligationStorage { 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}
59
60impl<'tcx> ObligationStorage<'tcx> {
61 fn register(
62 &mut self,
63 obligation: PredicateObligation<'tcx>,
64 stalled_on: Option<GoalStalledOn<TyCtxt<'tcx>>>,
65 ) {
66 self.pending.push((obligation, stalled_on));
67 }
68
69 fn has_pending_obligations(&self) -> bool {
70 !self.pending.is_empty()
71 }
72
73 fn clone_pending(&self) -> PredicateObligations<'tcx> {
74 self.pending.iter().map(|(o, _)| o.clone()).collect()
75 }
76
77 fn clone_pending_filtered<F>(&self, f: F) -> PredicateObligations<'tcx>
78 where
79 F: FnMut(&&(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)) -> bool,
80 {
81 self.pending.iter().filter(f).map(|(o, _)| o.clone()).collect()
82 }
83
84 fn drain_pending(
85 &mut self,
86 cond: impl Fn(&PredicateObligation<'tcx>, &Option<GoalStalledOn<TyCtxt<'tcx>>>) -> bool,
87 ) -> PendingObligations<'tcx> {
88 let (unstalled, pending) =
89 mem::take(&mut self.pending).into_iter().partition(|(o, s)| cond(o, s));
90 self.pending = pending;
91 unstalled
92 }
93}
94
95impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
96 pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx, E> {
97 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!(
98 infcx.next_trait_solver(),
99 "new trait solver fulfillment context created when \
100 infcx is set up for old trait solver"
101 );
102 FulfillmentCtxt {
103 obligations: Default::default(),
104 usable_in_snapshot: infcx.num_open_snapshots(),
105 _errors: PhantomData,
106 }
107 }
108
109 fn inspect_evaluated_obligation(
110 infcx: &InferCtxt<'tcx>,
111 obligation: &PredicateObligation<'tcx>,
112 result: &Result<GoalEvaluation<TyCtxt<'tcx>>, NoSolution>,
113 ) {
114 if let Some(inspector) = infcx.obligation_inspector.get() {
115 let result = match result {
116 Ok(GoalEvaluation { certainty, .. }) => Ok(*certainty),
117 Err(NoSolution) => Err(NoSolution),
118 };
119 (inspector)(infcx, &obligation, result);
120 }
121 }
122}
123
124impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>
125where
126 E: 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/b6eeb93c992027a03e1fd120b6652546396b142c/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))]
129 fn register_predicate_obligation(
130 &mut self,
131 infcx: &InferCtxt<'tcx>,
132 obligation: PredicateObligation<'tcx>,
133 ) {
134 assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
135
136 let delegate = <&SolverDelegate<'tcx>>::from(infcx);
137 if let Some(GoalEvaluation { goal: _, certainty, has_changed: _, stalled_on }) =
138 compute_goal_fast_path(delegate, obligation.as_goal(), obligation.cause.span)
139 {
140 match certainty {
143 Certainty::Yes => {}
144 Certainty::Maybe(_) => {
145 self.obligations.register(obligation, stalled_on);
146 }
147 }
148 } else {
149 self.obligations.register(obligation, None);
150 }
151 }
152
153 #[inline]
154 fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> TraitErrors<E> {
155 if self.obligations.pending.is_empty() {
156 TraitErrors::NoErrors
159 } else {
160 TraitErrors::HasErrors(collect_remaining_errors_impl(self, infcx))
161 }
162 }
163
164 fn try_evaluate_obligations(&mut self, infcx: &InferCtxt<'tcx>) -> TraitErrors<E> {
165 {
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());
166 let mut errors = TraitErrors::NoErrors;
167 let delegate = <&SolverDelegate<'tcx>>::from(infcx);
168 loop {
169 let mut any_changed = false;
170
171 self.obligations.pending.retain_mut(|(obligation, opt_stalled_on)| {
172 if let Some(stalled_on) = opt_stalled_on
175 && delegate.goal_remains_stalled(stalled_on)
176 {
177 return true;
178 }
179
180 let result = delegate.evaluate_root_goal(
181 obligation.as_goal(),
182 obligation.cause.span,
183 opt_stalled_on.take(),
184 );
185 Self::inspect_evaluated_obligation(infcx, &obligation, &result);
186 let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result {
187 Ok(result) => result,
188 Err(NoSolution) => {
189 errors.push(E::from_solver_error(
190 infcx,
191 NextSolverError::TrueError(obligation.clone()),
192 ));
193 return false;
194 }
195 };
196
197 obligation.predicate = goal.predicate;
201 if has_changed == HasChanged::Yes {
202 if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {
203 infcx.err_ctxt().report_overflow_obligation(obligation, true);
214 } else {
215 obligation.recursion_depth += 1;
222 any_changed = true;
223 }
224 }
225
226 match certainty {
227 Certainty::Yes => {
228 if infcx.in_hir_typeck
240 && (obligation.has_non_region_infer() || obligation.has_free_regions())
241 {
242 infcx.push_hir_typeck_potentially_region_dependent_goal(
243 obligation.clone(),
244 );
245 }
246 false
247 }
248 Certainty::Maybe(_) => {
249 *opt_stalled_on = stalled_on;
252 true
253 }
254 }
255 });
256
257 if !any_changed {
258 break;
259 }
260 }
261
262 errors
263 }
264
265 fn has_pending_obligations(&self) -> bool {
266 self.obligations.has_pending_obligations()
267 }
268
269 fn pending_obligations(&self) -> PredicateObligations<'tcx> {
270 self.obligations.clone_pending()
271 }
272
273 fn pending_obligations_potentially_referencing_sub_root(
274 &self,
275 infcx: &InferCtxt<'tcx>,
276 vid: ty::TyVid,
277 ) -> PredicateObligations<'tcx> {
278 if infcx.tcx.disable_trait_solver_fast_paths() {
280 return self.obligations.clone_pending();
281 }
282 self.obligations.clone_pending_filtered(|(_, stalled_on)| {
283 let Some(stalled_on) = stalled_on else { return true };
284 stalled_on.stalled_vars.iter().filter_map(|arg| arg.as_type(infcx.tcx)).any(|ty| {
293 match *infcx.shallow_resolve(ty).kind() {
294 ty::Infer(ty::TyVar(tv)) => infcx.sub_unification_table_root_var(tv) == vid,
295 _ => true,
296 }
297 })
298 })
299 }
300
301 fn pending_obligations_potentially_referencing_float_infer(
302 &self,
303 infcx: &InferCtxt<'tcx>,
304 ) -> PredicateObligations<'tcx> {
305 if infcx.tcx.disable_trait_solver_fast_paths() {
307 return self.obligations.clone_pending();
308 }
309
310 self.obligations.clone_pending_filtered(|(_, stalled_on)| {
311 let Some(stalled_on) = stalled_on else { return true };
312 stalled_on
315 .stalled_vars
316 .iter()
317 .filter_map(|arg| arg.as_type(infcx.tcx))
318 .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(_))))
319 })
320 }
321
322 fn drain_stalled_obligations_for_coroutines(
323 &mut self,
324 infcx: &InferCtxt<'tcx>,
325 ) -> PredicateObligations<'tcx> {
326 let stalled_coroutines = match infcx.typing_mode_raw().assert_not_erased() {
327 TypingMode::Typeck { defining_opaque_types_and_generators } => {
328 defining_opaque_types_and_generators
329 }
330 TypingMode::Coherence
331 | TypingMode::PostTypeckUntilBorrowck { defining_opaque_types: _ }
332 | TypingMode::PostBorrowck { defined_opaque_types: _ }
333 | TypingMode::Reflection
334 | TypingMode::PostAnalysis
335 | TypingMode::Codegen => return Default::default(),
336 };
337
338 if stalled_coroutines.is_empty() {
339 return Default::default();
340 }
341
342 self.obligations
343 .drain_pending(|_, stalled_on| {
344 stalled_on.as_ref().is_some_and(|s| {
345 match s.stalled_maybe_info.stalled_on_coroutines {
346 StalledOnCoroutines::Yes => true,
347 StalledOnCoroutines::No => false,
348 }
349 })
350 })
351 .into_iter()
352 .map(|(o, _)| o)
353 .collect()
354 }
355}
356
357#[cold]
358#[inline(never)]
359fn collect_remaining_errors_impl<'tcx, E>(
360 cx: &mut FulfillmentCtxt<'tcx, E>,
361 infcx: &InferCtxt<'tcx>,
362) -> ThinVec<E>
363where
364 E: FromSolverError<'tcx, NextSolverError<'tcx>>,
365{
366 cx.obligations
367 .pending
368 .drain(..)
369 .map(|(obligation, _)| NextSolverError::Ambiguity(obligation))
370 .map(|e| E::from_solver_error(infcx, e))
371 .collect()
372}
373
374pub enum NextSolverError<'tcx> {
375 TrueError(PredicateObligation<'tcx>),
376 Ambiguity(PredicateObligation<'tcx>),
377}
378
379impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
380 fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
381 match error {
382 NextSolverError::TrueError(obligation) => {
383 fulfillment_error_for_no_solution(infcx, obligation)
384 }
385 NextSolverError::Ambiguity(obligation) => {
386 fulfillment_error_for_stalled(infcx, obligation)
387 }
388 }
389 }
390}
391
392impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
393 fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
394 match error {
395 NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
396 NextSolverError::Ambiguity(_) => ScrubbedTraitError::Ambiguity,
397 }
398 }
399}
400
401#[cfg(target_pointer_width = "64")]
403mod size_asserts {
404 use rustc_data_structures::static_assert_size;
405
406 use super::*;
407 const _: [(); 104] =
[();
::std::mem::size_of::<(PredicateObligation<'_>,
Option<GoalStalledOn<TyCtxt<'_>>>)>()];static_assert_size!((PredicateObligation<'_>, Option<GoalStalledOn<TyCtxt<'_>>>), 104);
413 }