1use std::marker::PhantomData;
2use std::mem;
3use std::ops::ControlFlow;
45use rustc_data_structures::thinvec::ExtractIf;
6use rustc_hir::def_id::LocalDefId;
7use rustc_infer::infer::InferCtxt;
8use rustc_infer::traits::query::NoSolution;
9use rustc_infer::traits::{
10FromSolverError, PredicateObligation, PredicateObligations, TraitEngine,
11};
12use rustc_middle::ty::{
13self, DelayedSet, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
14 TypingMode,
15};
16use rustc_next_trait_solver::delegate::SolverDelegateas _;
17use rustc_next_trait_solver::solve::{
18GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExtas _,
19};
20use rustc_span::Span;
21use thin_vec::ThinVec;
22use tracing::instrument;
2324use self::derive_errors::*;
25use super::Certainty;
26use super::delegate::SolverDelegate;
27use super::inspect::{self, InferCtxtProofTreeExt};
28use crate::traits::{FulfillmentError, ScrubbedTraitError};
2930mod derive_errors;
3132// FIXME: Do we need to use a `ThinVec` here?
33type PendingObligations<'tcx> =
34ThinVec<(PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)>;
3536/// A trait engine using the new trait solver.
37///
38/// This is mostly identical to how `evaluate_all` works inside of the
39/// solver, except that the requirements are slightly different.
40///
41/// Unlike `evaluate_all` it is possible to add new obligations later on
42/// and we also have to track diagnostics information by using `Obligation`
43/// instead of `Goal`.
44///
45/// It is also likely that we want to use slightly different datastructures
46/// here as this will have to deal with far more root goals than `evaluate_all`.
47pub struct FulfillmentCtxt<'tcx, E: 'tcx> {
48 obligations: ObligationStorage<'tcx>,
4950/// The snapshot in which this context was created. Using the context
51 /// outside of this snapshot leads to subtle bugs if the snapshot
52 /// gets rolled back. Because of this we explicitly check that we only
53 /// use the context in exactly this snapshot.
54usable_in_snapshot: usize,
55 _errors: PhantomData<E>,
56}
5758#[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)]
59struct ObligationStorage<'tcx> {
60/// Obligations which resulted in an overflow in fulfillment itself.
61 ///
62 /// We cannot eagerly return these as error so we instead store them here
63 /// to avoid recomputing them each time `try_evaluate_obligations` is called.
64 /// This also allows us to return the correct `FulfillmentError` for them.
65overflowed: Vec<PredicateObligation<'tcx>>,
66 pending: PendingObligations<'tcx>,
67}
6869impl<'tcx> ObligationStorage<'tcx> {
70fn register(
71&mut self,
72 obligation: PredicateObligation<'tcx>,
73 stalled_on: Option<GoalStalledOn<TyCtxt<'tcx>>>,
74 ) {
75self.pending.push((obligation, stalled_on));
76 }
7778fn has_pending_obligations(&self) -> bool {
79 !self.pending.is_empty() || !self.overflowed.is_empty()
80 }
8182fn clone_pending(&self) -> PredicateObligations<'tcx> {
83let mut obligations: PredicateObligations<'tcx> =
84self.pending.iter().map(|(o, _)| o.clone()).collect();
85obligations.extend(self.overflowed.iter().cloned());
86obligations87 }
8889fn drain_pending(
90&mut self,
91 cond: impl Fn(&PredicateObligation<'tcx>) -> bool,
92 ) -> PendingObligations<'tcx> {
93let (unstalled, pending) =
94 mem::take(&mut self.pending).into_iter().partition(|(o, _)| cond(o));
95self.pending = pending;
96unstalled97 }
9899fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {
100infcx.probe(|_| {
101// IMPORTANT: we must not use solve any inference variables in the obligations
102 // as this is all happening inside of a probe. We use a probe to make sure
103 // we get all obligations involved in the overflow. We pretty much check: if
104 // we were to do another step of `try_evaluate_obligations`, which goals would
105 // change.
106 // FIXME: <https://github.com/Gankra/thin-vec/pull/66> is merged, this can be removed.
107self.overflowed.extend(
108ExtractIf::new(&mut self.pending, |(o, stalled_on)| {
109let goal = o.as_goal();
110let result = <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal(
111goal,
112o.cause.span,
113stalled_on.take(),
114 );
115#[allow(non_exhaustive_omitted_patterns)] match result {
Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }) => true,
_ => false,
}matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }))116 })
117 .map(|(o, _)| o),
118 );
119 })
120 }
121}
122123impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
124pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx, E> {
125if !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!(
126 infcx.next_trait_solver(),
127"new trait solver fulfillment context created when \
128 infcx is set up for old trait solver"
129);
130FulfillmentCtxt {
131 obligations: Default::default(),
132 usable_in_snapshot: infcx.num_open_snapshots(),
133 _errors: PhantomData,
134 }
135 }
136137fn inspect_evaluated_obligation(
138&self,
139 infcx: &InferCtxt<'tcx>,
140 obligation: &PredicateObligation<'tcx>,
141 result: &Result<GoalEvaluation<TyCtxt<'tcx>>, NoSolution>,
142 ) {
143if let Some(inspector) = infcx.obligation_inspector.get() {
144let result = match result {
145Ok(GoalEvaluation { certainty, .. }) => Ok(*certainty),
146Err(NoSolution) => Err(NoSolution),
147 };
148 (inspector)(infcx, &obligation, result);
149 }
150 }
151}
152153impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>
154where
155E: FromSolverError<'tcx, NextSolverError<'tcx>>,
156{
157#[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(157u32),
::tracing_core::__macro_support::Option::Some("rustc_trait_selection::solve::fulfill"),
::tracing_core::field::FieldSet::new(&["obligation"],
::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};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&obligation)
as &dyn 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);
}
}
};
self.obligations.register(obligation, None);
}
}
}#[instrument(level = "trace", skip(self, infcx))]158fn register_predicate_obligation(
159&mut self,
160 infcx: &InferCtxt<'tcx>,
161 obligation: PredicateObligation<'tcx>,
162 ) {
163assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
164self.obligations.register(obligation, None);
165 }
166167fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
168self.obligations
169 .pending
170 .drain(..)
171 .map(|(obligation, _)| NextSolverError::Ambiguity(obligation))
172 .chain(
173self.obligations
174 .overflowed
175 .drain(..)
176 .map(|obligation| NextSolverError::Overflow(obligation)),
177 )
178 .map(|e| E::from_solver_error(infcx, e))
179 .collect()
180 }
181182fn try_evaluate_obligations(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
183match (&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());
184let mut errors = Vec::new();
185loop {
186let mut any_changed = false;
187for (mut obligation, stalled_on) in self.obligations.drain_pending(|_| true) {
188if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {
189self.obligations.on_fulfillment_overflow(infcx);
190// Only return true errors that we have accumulated while processing.
191return errors;
192 }
193194let goal = obligation.as_goal();
195let delegate = <&SolverDelegate<'tcx>>::from(infcx);
196if let Some(certainty) =
197 delegate.compute_goal_fast_path(goal, obligation.cause.span)
198 {
199match certainty {
200// This fast path doesn't depend on region identity so it doesn't
201 // matter if the goal contains inference variables or not, so we
202 // don't need to call `push_hir_typeck_potentially_region_dependent_goal`
203 // here.
204 //
205 // Only goals proven via the trait solver should be region dependent.
206Certainty::Yes => {}
207 Certainty::Maybe { .. } => {
208self.obligations.register(obligation, None);
209 }
210 }
211continue;
212 }
213214let result = delegate.evaluate_root_goal(goal, obligation.cause.span, stalled_on);
215self.inspect_evaluated_obligation(infcx, &obligation, &result);
216let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result {
217Ok(result) => result,
218Err(NoSolution) => {
219 errors.push(E::from_solver_error(
220 infcx,
221 NextSolverError::TrueError(obligation),
222 ));
223continue;
224 }
225 };
226227// We've resolved the goal in `evaluate_root_goal`, avoid redoing this work
228 // in the next iteration. This does not resolve the inference variables
229 // constrained by evaluating the goal.
230obligation.predicate = goal.predicate;
231if has_changed == HasChanged::Yes {
232// We increment the recursion depth here to track the number of times
233 // this goal has resulted in inference progress. This doesn't precisely
234 // model the way that we track recursion depth in the old solver due
235 // to the fact that we only process root obligations, but it is a good
236 // approximation and should only result in fulfillment overflow in
237 // pathological cases.
238obligation.recursion_depth += 1;
239 any_changed = true;
240 }
241242match certainty {
243 Certainty::Yes => {
244// Goals may depend on structural identity. Region uniquification at the
245 // start of MIR borrowck may cause things to no longer be so, potentially
246 // causing an ICE.
247 //
248 // While we uniquify root goals in HIR this does not handle cases where
249 // regions are hidden inside of a type or const inference variable.
250 //
251 // FIXME(-Znext-solver): This does not handle inference variables hidden
252 // inside of an opaque type, e.g. if there's `Opaque = (?x, ?x)` in the
253 // storage, we can also rely on structural identity of `?x` even if we
254 // later uniquify it in MIR borrowck.
255if infcx.in_hir_typeck
256 && (obligation.has_non_region_infer() || obligation.has_free_regions())
257 {
258 infcx.push_hir_typeck_potentially_region_dependent_goal(obligation);
259 }
260 }
261 Certainty::Maybe { .. } => self.obligations.register(obligation, stalled_on),
262 }
263 }
264265if !any_changed {
266break;
267 }
268 }
269270errors271 }
272273fn has_pending_obligations(&self) -> bool {
274self.obligations.has_pending_obligations()
275 }
276277fn pending_obligations(&self) -> PredicateObligations<'tcx> {
278self.obligations.clone_pending()
279 }
280281fn drain_stalled_obligations_for_coroutines(
282&mut self,
283 infcx: &InferCtxt<'tcx>,
284 ) -> PredicateObligations<'tcx> {
285let stalled_coroutines = match infcx.typing_mode() {
286 TypingMode::Analysis { defining_opaque_types_and_generators } => {
287defining_opaque_types_and_generators288 }
289 TypingMode::Coherence290 | TypingMode::Borrowck { defining_opaque_types: _ }
291 | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ }
292 | TypingMode::PostAnalysis => return Default::default(),
293 };
294295if stalled_coroutines.is_empty() {
296return Default::default();
297 }
298299self.obligations
300 .drain_pending(|obl| {
301infcx.probe(|_| {
302infcx303 .visit_proof_tree(
304obl.as_goal(),
305&mut StalledOnCoroutines {
306stalled_coroutines,
307 span: obl.cause.span,
308 cache: Default::default(),
309 },
310 )
311 .is_break()
312 })
313 })
314 .into_iter()
315 .map(|(o, _)| o)
316 .collect()
317 }
318}
319320/// Detect if a goal is stalled on a coroutine that is owned by the current typeck root.
321///
322/// This function can (erroneously) fail to detect a predicate, i.e. it doesn't need to
323/// be complete. However, this will lead to ambiguity errors, so we want to make it
324/// accurate.
325///
326/// This function can be also return false positives, which will lead to poor diagnostics
327/// so we want to keep this visitor *precise* too.
328pub struct StalledOnCoroutines<'tcx> {
329pub stalled_coroutines: &'tcx ty::List<LocalDefId>,
330pub span: Span,
331pub cache: DelayedSet<Ty<'tcx>>,
332}
333334impl<'tcx> inspect::ProofTreeVisitor<'tcx> for StalledOnCoroutines<'tcx> {
335type Result = ControlFlow<()>;
336337fn span(&self) -> rustc_span::Span {
338self.span
339 }
340341fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
342inspect_goal.goal().predicate.visit_with(self)?;
343344if let Some(candidate) = inspect_goal.unique_applicable_candidate() {
345candidate.visit_nested_no_probe(self)
346 } else {
347 ControlFlow::Continue(())
348 }
349 }
350}
351352impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for StalledOnCoroutines<'tcx> {
353type Result = ControlFlow<()>;
354355fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
356if !self.cache.insert(ty) {
357return ControlFlow::Continue(());
358 }
359360if let ty::Coroutine(def_id, _) = *ty.kind()
361 && def_id.as_local().is_some_and(|def_id| self.stalled_coroutines.contains(&def_id))
362 {
363 ControlFlow::Break(())
364 } else if ty.has_coroutines() {
365ty.super_visit_with(self)
366 } else {
367 ControlFlow::Continue(())
368 }
369 }
370}
371372pub enum NextSolverError<'tcx> {
373 TrueError(PredicateObligation<'tcx>),
374 Ambiguity(PredicateObligation<'tcx>),
375 Overflow(PredicateObligation<'tcx>),
376}
377378impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
379fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
380match error {
381 NextSolverError::TrueError(obligation) => {
382fulfillment_error_for_no_solution(infcx, obligation)
383 }
384 NextSolverError::Ambiguity(obligation) => {
385fulfillment_error_for_stalled(infcx, obligation)
386 }
387 NextSolverError::Overflow(obligation) => {
388fulfillment_error_for_overflow(infcx, obligation)
389 }
390 }
391 }
392}
393394impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
395fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
396match error {
397 NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
398 NextSolverError::Ambiguity(_) | NextSolverError::Overflow(_) => {
399 ScrubbedTraitError::Ambiguity400 }
401 }
402 }
403}