rustc_trait_selection/solve/
fulfill.rs
1use std::marker::PhantomData;
2use std::mem;
3
4use rustc_data_structures::thinvec::ExtractIf;
5use rustc_infer::infer::InferCtxt;
6use rustc_infer::traits::query::NoSolution;
7use rustc_infer::traits::{
8 FromSolverError, PredicateObligation, PredicateObligations, TraitEngine,
9};
10use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _};
11use tracing::instrument;
12
13use self::derive_errors::*;
14use super::Certainty;
15use super::delegate::SolverDelegate;
16use crate::traits::{FulfillmentError, ScrubbedTraitError};
17
18mod derive_errors;
19
20pub struct FulfillmentCtxt<'tcx, E: 'tcx> {
32 obligations: ObligationStorage<'tcx>,
33
34 usable_in_snapshot: usize,
39 _errors: PhantomData<E>,
40}
41
42#[derive(Default)]
43struct ObligationStorage<'tcx> {
44 overflowed: PredicateObligations<'tcx>,
50 pending: PredicateObligations<'tcx>,
51}
52
53impl<'tcx> ObligationStorage<'tcx> {
54 fn register(&mut self, obligation: PredicateObligation<'tcx>) {
55 self.pending.push(obligation);
56 }
57
58 fn clone_pending(&self) -> PredicateObligations<'tcx> {
59 let mut obligations = self.pending.clone();
60 obligations.extend(self.overflowed.iter().cloned());
61 obligations
62 }
63
64 fn take_pending(&mut self) -> PredicateObligations<'tcx> {
65 let mut obligations = mem::take(&mut self.pending);
66 obligations.append(&mut self.overflowed);
67 obligations
68 }
69
70 fn unstalled_for_select(&mut self) -> impl Iterator<Item = PredicateObligation<'tcx>> {
71 mem::take(&mut self.pending).into_iter()
72 }
73
74 fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {
75 infcx.probe(|_| {
76 self.overflowed.extend(ExtractIf::new(&mut self.pending, |o| {
83 let goal = o.clone().into();
84 let result = <&SolverDelegate<'tcx>>::from(infcx)
85 .evaluate_root_goal(goal, GenerateProofTree::No, o.cause.span)
86 .0;
87 matches!(result, Ok((HasChanged::Yes, _)))
88 }));
89 })
90 }
91}
92
93impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
94 pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx, E> {
95 assert!(
96 infcx.next_trait_solver(),
97 "new trait solver fulfillment context created when \
98 infcx is set up for old trait solver"
99 );
100 FulfillmentCtxt {
101 obligations: Default::default(),
102 usable_in_snapshot: infcx.num_open_snapshots(),
103 _errors: PhantomData,
104 }
105 }
106
107 fn inspect_evaluated_obligation(
108 &self,
109 infcx: &InferCtxt<'tcx>,
110 obligation: &PredicateObligation<'tcx>,
111 result: &Result<(HasChanged, Certainty), NoSolution>,
112 ) {
113 if let Some(inspector) = infcx.obligation_inspector.get() {
114 let result = match result {
115 Ok((_, c)) => Ok(*c),
116 Err(NoSolution) => Err(NoSolution),
117 };
118 (inspector)(infcx, &obligation, result);
119 }
120 }
121}
122
123impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>
124where
125 E: FromSolverError<'tcx, NextSolverError<'tcx>>,
126{
127 #[instrument(level = "trace", skip(self, infcx))]
128 fn register_predicate_obligation(
129 &mut self,
130 infcx: &InferCtxt<'tcx>,
131 obligation: PredicateObligation<'tcx>,
132 ) {
133 assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
134 self.obligations.register(obligation);
135 }
136
137 fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
138 self.obligations
139 .pending
140 .drain(..)
141 .map(|obligation| NextSolverError::Ambiguity(obligation))
142 .chain(
143 self.obligations
144 .overflowed
145 .drain(..)
146 .map(|obligation| NextSolverError::Overflow(obligation)),
147 )
148 .map(|e| E::from_solver_error(infcx, e))
149 .collect()
150 }
151
152 fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
153 assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
154 let mut errors = Vec::new();
155 for i in 0.. {
156 if !infcx.tcx.recursion_limit().value_within_limit(i) {
157 self.obligations.on_fulfillment_overflow(infcx);
158 return errors;
160 }
161
162 let mut has_changed = false;
163 for obligation in self.obligations.unstalled_for_select() {
164 let goal = obligation.clone().into();
165 let result = <&SolverDelegate<'tcx>>::from(infcx)
166 .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span)
167 .0;
168 self.inspect_evaluated_obligation(infcx, &obligation, &result);
169 let (changed, certainty) = match result {
170 Ok(result) => result,
171 Err(NoSolution) => {
172 errors.push(E::from_solver_error(
173 infcx,
174 NextSolverError::TrueError(obligation),
175 ));
176 continue;
177 }
178 };
179
180 if changed == HasChanged::Yes {
181 has_changed = true;
182 }
183
184 match certainty {
185 Certainty::Yes => {}
186 Certainty::Maybe(_) => self.obligations.register(obligation),
187 }
188 }
189
190 if !has_changed {
191 break;
192 }
193 }
194
195 errors
196 }
197
198 fn has_pending_obligations(&self) -> bool {
199 !self.obligations.pending.is_empty() || !self.obligations.overflowed.is_empty()
200 }
201
202 fn pending_obligations(&self) -> PredicateObligations<'tcx> {
203 self.obligations.clone_pending()
204 }
205
206 fn drain_unstalled_obligations(&mut self, _: &InferCtxt<'tcx>) -> PredicateObligations<'tcx> {
207 self.obligations.take_pending()
208 }
209}
210
211pub enum NextSolverError<'tcx> {
212 TrueError(PredicateObligation<'tcx>),
213 Ambiguity(PredicateObligation<'tcx>),
214 Overflow(PredicateObligation<'tcx>),
215}
216
217impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
218 fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
219 match error {
220 NextSolverError::TrueError(obligation) => {
221 fulfillment_error_for_no_solution(infcx, obligation)
222 }
223 NextSolverError::Ambiguity(obligation) => {
224 fulfillment_error_for_stalled(infcx, obligation)
225 }
226 NextSolverError::Overflow(obligation) => {
227 fulfillment_error_for_overflow(infcx, obligation)
228 }
229 }
230 }
231}
232
233impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
234 fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
235 match error {
236 NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
237 NextSolverError::Ambiguity(_) | NextSolverError::Overflow(_) => {
238 ScrubbedTraitError::Ambiguity
239 }
240 }
241 }
242}