1use rustc_ast_ir::try_visit;
2use rustc_next_trait_solver::{
3 canonical::instantiate_canonical_state,
4 resolve::eager_resolve_vars,
5 solve::{SolverDelegateEvalExt, inspect},
6};
7use rustc_type_ir::{
8 VisitorResult,
9 inherent::IntoKind,
10 solve::{Certainty, GoalSource, MaybeCause, MaybeInfo, NoSolution},
11};
12
13use crate::{
14 Span,
15 next_solver::{
16 DbInterner, GenericArg, GenericArgs, Goal, NormalizesTo, ParamEnv, Predicate,
17 PredicateKind, QueryResult, SolverContext, Term,
18 fulfill::NextSolverError,
19 infer::{
20 InferCtxt,
21 traits::{Obligation, ObligationCause},
22 },
23 obligation_ctxt::ObligationCtxt,
24 },
25};
26
27pub(crate) use rustc_next_trait_solver::solve::inspect::*;
28
29pub(crate) struct InspectConfig {
30 pub(crate) max_depth: usize,
31}
32
33pub(crate) struct InspectGoal<'a, 'db> {
34 infcx: &'a SolverContext<'db>,
35 depth: usize,
36 orig_values: Vec<GenericArg<'db>>,
37 goal: Goal<'db, Predicate<'db>>,
38 result: Result<Certainty, NoSolution>,
39 final_revision: inspect::Probe<DbInterner<'db>>,
40 normalizes_to_term_hack: Option<NormalizesToTermHack<'db>>,
41 source: GoalSource,
42}
43
44impl<'a, 'db> std::fmt::Debug for InspectGoal<'a, 'db> {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 f.debug_struct("InspectGoal")
47 .field("depth", &self.depth)
48 .field("orig_values", &self.orig_values)
49 .field("goal", &self.goal)
50 .field("result", &self.result)
51 .field("final_revision", &self.final_revision)
52 .field("normalizes_to_term_hack", &self.normalizes_to_term_hack)
53 .field("source", &self.source)
54 .finish()
55 }
56}
57
58impl<'a, 'db> std::fmt::Debug for InspectCandidate<'a, 'db> {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 f.debug_struct("InspectCandidate")
61 .field("kind", &self.kind)
62 .field("steps", &self.steps)
63 .field("final_state", &self.final_state)
64 .field("result", &self.result)
65 .field("shallow_certainty", &self.shallow_certainty)
66 .finish()
67 }
68}
69
70#[derive(Debug, Copy, Clone)]
80pub(crate) struct NormalizesToTermHack<'db> {
81 term: Term<'db>,
82 unconstrained_term: Term<'db>,
83}
84
85impl<'db> NormalizesToTermHack<'db> {
86 fn constrain_and(
90 &self,
91 infcx: &InferCtxt<'db>,
92 param_env: ParamEnv<'db>,
93 f: impl FnOnce(&mut ObligationCtxt<'_, 'db>),
94 ) -> Result<Certainty, NoSolution> {
95 let mut ocx = ObligationCtxt::new(infcx);
96 ocx.eq(&ObligationCause::dummy(), param_env, self.term, self.unconstrained_term)?;
97 f(&mut ocx);
98 let errors = ocx.evaluate_obligations_error_on_ambiguity();
99 if errors.is_empty() {
100 Ok(Certainty::Yes)
101 } else if errors.iter().all(|e| !matches!(e, NextSolverError::TrueError(_))) {
102 Ok(Certainty::AMBIGUOUS)
103 } else {
104 Err(NoSolution)
105 }
106 }
107}
108
109pub(crate) struct InspectCandidate<'a, 'db> {
110 goal: &'a InspectGoal<'a, 'db>,
111 kind: inspect::ProbeKind<DbInterner<'db>>,
112 steps: Vec<&'a inspect::ProbeStep<DbInterner<'db>>>,
113 final_state: inspect::CanonicalState<DbInterner<'db>, ()>,
114 result: QueryResult<'db>,
115 shallow_certainty: Certainty,
116}
117
118impl<'a, 'db> InspectCandidate<'a, 'db> {
119 pub(crate) fn kind(&self) -> inspect::ProbeKind<DbInterner<'db>> {
120 self.kind
121 }
122
123 pub(crate) fn result(&self) -> Result<Certainty, NoSolution> {
124 self.result.map(|c| c.value.certainty)
125 }
126
127 pub(crate) fn goal(&self) -> &'a InspectGoal<'a, 'db> {
128 self.goal
129 }
130
131 pub(crate) fn shallow_certainty(&self) -> Certainty {
140 self.shallow_certainty
141 }
142
143 pub(crate) fn visit_nested_no_probe<V: ProofTreeVisitor<'db>>(
147 &self,
148 visitor: &mut V,
149 ) -> V::Result {
150 for goal in self.instantiate_nested_goals(visitor.span()) {
151 try_visit!(goal.visit_with(visitor));
152 }
153
154 V::Result::output()
155 }
156
157 pub(crate) fn instantiate_nested_goals(&self, span: Span) -> Vec<InspectGoal<'a, 'db>> {
162 let infcx = self.goal.infcx;
163 let param_env = self.goal.goal.param_env;
164 let mut orig_values = self.goal.orig_values.to_vec();
165
166 let mut instantiated_goals = vec![];
167 for step in &self.steps {
168 match **step {
169 inspect::ProbeStep::AddGoal(source, goal) => instantiated_goals.push((
170 source,
171 instantiate_canonical_state(infcx, span, param_env, &mut orig_values, goal),
172 )),
173 inspect::ProbeStep::RecordImplArgs { .. } => {}
174 inspect::ProbeStep::MakeCanonicalResponse { .. }
175 | inspect::ProbeStep::NestedProbe(_) => unreachable!(),
176 }
177 }
178
179 let () =
180 instantiate_canonical_state(infcx, span, param_env, &mut orig_values, self.final_state);
181
182 if let Some(term_hack) = &self.goal.normalizes_to_term_hack {
183 let _ = term_hack.constrain_and(infcx, param_env, |_| {});
187 }
188
189 instantiated_goals
190 .into_iter()
191 .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span))
192 .collect()
193 }
194
195 pub(crate) fn instantiate_impl_args(&self, span: Span) -> GenericArgs<'db> {
199 let infcx = self.goal.infcx;
200 let param_env = self.goal.goal.param_env;
201 let mut orig_values = self.goal.orig_values.to_vec();
202
203 for step in &self.steps {
204 match **step {
205 inspect::ProbeStep::RecordImplArgs { impl_args } => {
206 let impl_args = instantiate_canonical_state(
207 infcx,
208 span,
209 param_env,
210 &mut orig_values,
211 impl_args,
212 );
213
214 let () = instantiate_canonical_state(
215 infcx,
216 span,
217 param_env,
218 &mut orig_values,
219 self.final_state,
220 );
221
222 assert!(
224 self.goal.normalizes_to_term_hack.is_none(),
225 "cannot use `instantiate_impl_args` with a `NormalizesTo` goal"
226 );
227
228 return eager_resolve_vars(infcx, impl_args);
229 }
230 inspect::ProbeStep::AddGoal(..) => {}
231 inspect::ProbeStep::MakeCanonicalResponse { .. }
232 | inspect::ProbeStep::NestedProbe(_) => unreachable!(),
233 }
234 }
235
236 panic!("expected impl args probe step for `instantiate_impl_args`");
237 }
238
239 pub(crate) fn instantiate_proof_tree_for_nested_goal(
240 &self,
241 source: GoalSource,
242 goal: Goal<'db, Predicate<'db>>,
243 span: Span,
244 ) -> InspectGoal<'a, 'db> {
245 let infcx = self.goal.infcx;
246 match goal.predicate.kind().no_bound_vars() {
247 Some(PredicateKind::NormalizesTo(NormalizesTo { alias, term })) => {
248 let unconstrained_term = infcx.next_term_var_of_kind(term, span);
249 let goal =
250 goal.with(infcx.interner, NormalizesTo { alias, term: unconstrained_term });
251 let normalizes_to_term_hack = NormalizesToTermHack { term, unconstrained_term };
258 let (proof_tree, nested_goals_result) = infcx.probe(|_| {
259 let (nested, proof_tree) = infcx.evaluate_root_goal_for_proof_tree(goal, span);
264 let nested_goals_result = nested.and_then(|nested| {
265 normalizes_to_term_hack.constrain_and(
266 infcx,
267 proof_tree.uncanonicalized_goal.param_env,
268 |ocx| {
269 ocx.register_obligations(nested.0.into_iter().map(|(_, goal)| {
270 Obligation::new(
271 infcx.interner,
272 ObligationCause::dummy(),
273 goal.param_env,
274 goal.predicate,
275 )
276 }));
277 },
278 )
279 });
280 (proof_tree, nested_goals_result)
281 });
282 InspectGoal::new(
283 infcx,
284 self.goal.depth + 1,
285 proof_tree,
286 Some((normalizes_to_term_hack, nested_goals_result)),
287 source,
288 )
289 }
290 _ => {
291 let proof_tree =
297 infcx.probe(|_| infcx.evaluate_root_goal_for_proof_tree(goal, span).1);
298 InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
299 }
300 }
301 }
302
303 #[expect(dead_code, reason = "used in rustc")]
306 fn visit_nested_in_probe<V: ProofTreeVisitor<'db>>(&self, visitor: &mut V) -> V::Result {
307 self.goal.infcx.probe(|_| self.visit_nested_no_probe(visitor))
308 }
309}
310
311impl<'a, 'db> InspectGoal<'a, 'db> {
312 pub(crate) fn infcx(&self) -> &'a InferCtxt<'db> {
313 self.infcx
314 }
315
316 pub(crate) fn goal(&self) -> Goal<'db, Predicate<'db>> {
317 self.goal
318 }
319
320 pub(crate) fn result(&self) -> Result<Certainty, NoSolution> {
321 self.result
322 }
323
324 pub(crate) fn source(&self) -> GoalSource {
325 self.source
326 }
327
328 pub(crate) fn depth(&self) -> usize {
329 self.depth
330 }
331
332 fn candidates_recur(
333 &'a self,
334 candidates: &mut Vec<InspectCandidate<'a, 'db>>,
335 steps: &mut Vec<&'a inspect::ProbeStep<DbInterner<'db>>>,
336 probe: &'a inspect::Probe<DbInterner<'db>>,
337 ) {
338 let mut shallow_certainty = None;
339 for step in &probe.steps {
340 match *step {
341 inspect::ProbeStep::AddGoal(..) | inspect::ProbeStep::RecordImplArgs { .. } => {
342 steps.push(step)
343 }
344 inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => {
345 assert!(matches!(
346 shallow_certainty.replace(c),
347 None | Some(Certainty::Maybe(MaybeInfo {
348 cause: MaybeCause::Ambiguity,
349 ..
350 }))
351 ));
352 }
353 inspect::ProbeStep::NestedProbe(ref probe) => {
354 match probe.kind {
355 inspect::ProbeKind::ProjectionCompatibility
357 | inspect::ProbeKind::ShadowedEnvProbing => continue,
358
359 inspect::ProbeKind::NormalizedSelfTyAssembly
360 | inspect::ProbeKind::UnsizeAssembly
361 | inspect::ProbeKind::Root { .. }
362 | inspect::ProbeKind::TraitCandidate { .. }
363 | inspect::ProbeKind::OpaqueTypeStorageLookup { .. }
364 | inspect::ProbeKind::RigidAlias { .. } => {
365 let num_steps = steps.len();
369 self.candidates_recur(candidates, steps, probe);
370 steps.truncate(num_steps);
371 }
372 }
373 }
374 }
375 }
376
377 match probe.kind {
378 inspect::ProbeKind::ProjectionCompatibility
379 | inspect::ProbeKind::ShadowedEnvProbing => {
380 panic!()
381 }
382
383 inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly => {}
384
385 inspect::ProbeKind::Root { result }
388 | inspect::ProbeKind::TraitCandidate { source: _, result }
389 | inspect::ProbeKind::OpaqueTypeStorageLookup { result }
390 | inspect::ProbeKind::RigidAlias { result } => {
391 if let Some(shallow_certainty) = shallow_certainty {
394 candidates.push(InspectCandidate {
395 goal: self,
396 kind: probe.kind,
397 steps: steps.clone(),
398 final_state: probe.final_state,
399 shallow_certainty,
400 result,
401 });
402 }
403 }
404 }
405 }
406
407 pub(crate) fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'db>> {
408 let mut candidates = vec![];
409 let mut nested_goals = vec![];
410 self.candidates_recur(&mut candidates, &mut nested_goals, &self.final_revision);
411 candidates
412 }
413
414 pub(crate) fn unique_applicable_candidate(&'a self) -> Option<InspectCandidate<'a, 'db>> {
418 let mut candidates = self.candidates();
421 candidates.retain(|c| c.result().is_ok());
422 candidates.pop().filter(|_| candidates.is_empty())
423 }
424
425 pub(crate) fn new(
426 infcx: &'a InferCtxt<'db>,
427 depth: usize,
428 root: inspect::GoalEvaluation<DbInterner<'db>>,
429 term_hack_and_nested_certainty: Option<(
430 NormalizesToTermHack<'db>,
431 Result<Certainty, NoSolution>,
432 )>,
433 source: GoalSource,
434 ) -> Self {
435 let infcx = <&SolverContext<'db>>::from(infcx);
436
437 let inspect::GoalEvaluation { uncanonicalized_goal, orig_values, final_revision, result } =
438 root;
439 let result = result.and_then(|ok| {
442 let nested_goals_certainty =
443 term_hack_and_nested_certainty.map_or(Ok(Certainty::Yes), |(_, c)| c)?;
444 Ok(ok.value.certainty.and(nested_goals_certainty))
445 });
446
447 InspectGoal {
448 infcx,
449 depth,
450 orig_values,
451 goal: eager_resolve_vars(infcx, uncanonicalized_goal),
452 result,
453 final_revision,
454 normalizes_to_term_hack: term_hack_and_nested_certainty.map(|(n, _)| n),
455 source,
456 }
457 }
458
459 pub(crate) fn visit_with<V: ProofTreeVisitor<'db>>(&self, visitor: &mut V) -> V::Result {
460 if self.depth < visitor.config().max_depth {
461 try_visit!(visitor.visit_goal(self));
462 V::Result::output()
463 } else {
464 visitor.on_recursion_limit()
465 }
466 }
467}
468
469pub(crate) trait ProofTreeVisitor<'db> {
471 type Result: VisitorResult;
472
473 fn span(&self) -> Span;
474
475 fn config(&self) -> InspectConfig {
476 InspectConfig { max_depth: 10 }
477 }
478
479 fn visit_goal(&mut self, goal: &InspectGoal<'_, 'db>) -> Self::Result;
480
481 fn on_recursion_limit(&mut self) -> Self::Result {
482 Self::Result::output()
483 }
484}
485
486impl<'db> InferCtxt<'db> {
487 pub(crate) fn visit_proof_tree<V: ProofTreeVisitor<'db>>(
488 &self,
489 goal: Goal<'db, Predicate<'db>>,
490 visitor: &mut V,
491 ) -> V::Result {
492 self.visit_proof_tree_at_depth(goal, 0, visitor)
493 }
494
495 pub(crate) fn visit_proof_tree_at_depth<V: ProofTreeVisitor<'db>>(
496 &self,
497 goal: Goal<'db, Predicate<'db>>,
498 depth: usize,
499 visitor: &mut V,
500 ) -> V::Result {
501 let (_, proof_tree) = <&SolverContext<'db>>::from(self)
502 .evaluate_root_goal_for_proof_tree(goal, visitor.span());
503 visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc))
504 }
505}