rustc_trait_selection/solve/inspect/
analyse.rs1use std::assert_matches::assert_matches;
13
14use rustc_infer::infer::InferCtxt;
15use rustc_infer::traits::Obligation;
16use rustc_macros::extension;
17use rustc_middle::traits::ObligationCause;
18use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, NoSolution, QueryResult};
19use rustc_middle::ty::{TyCtxt, VisitorResult, try_visit};
20use rustc_middle::{bug, ty};
21use rustc_next_trait_solver::resolve::eager_resolve_vars;
22use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state};
23use rustc_next_trait_solver::solve::{GenerateProofTree, MaybeCause, SolverDelegateEvalExt as _};
24use rustc_span::Span;
25use tracing::instrument;
26
27use crate::solve::delegate::SolverDelegate;
28use crate::traits::ObligationCtxt;
29
30pub struct InspectConfig {
31 pub max_depth: usize,
32}
33
34pub struct InspectGoal<'a, 'tcx> {
35 infcx: &'a SolverDelegate<'tcx>,
36 depth: usize,
37 orig_values: Vec<ty::GenericArg<'tcx>>,
38 goal: Goal<'tcx, ty::Predicate<'tcx>>,
39 result: Result<Certainty, NoSolution>,
40 evaluation_kind: inspect::CanonicalGoalEvaluationKind<TyCtxt<'tcx>>,
41 normalizes_to_term_hack: Option<NormalizesToTermHack<'tcx>>,
42 source: GoalSource,
43}
44
45#[derive(Copy, Clone)]
55struct NormalizesToTermHack<'tcx> {
56 term: ty::Term<'tcx>,
57 unconstrained_term: ty::Term<'tcx>,
58}
59
60impl<'tcx> NormalizesToTermHack<'tcx> {
61 fn constrain_and(
65 &self,
66 infcx: &InferCtxt<'tcx>,
67 span: Span,
68 param_env: ty::ParamEnv<'tcx>,
69 f: impl FnOnce(&ObligationCtxt<'_, 'tcx>),
70 ) -> Result<Certainty, NoSolution> {
71 let ocx = ObligationCtxt::new(infcx);
72 ocx.eq(
73 &ObligationCause::dummy_with_span(span),
74 param_env,
75 self.term,
76 self.unconstrained_term,
77 )?;
78 f(&ocx);
79 let errors = ocx.select_all_or_error();
80 if errors.is_empty() {
81 Ok(Certainty::Yes)
82 } else if errors.iter().all(|e| !e.is_true_error()) {
83 Ok(Certainty::AMBIGUOUS)
84 } else {
85 Err(NoSolution)
86 }
87 }
88}
89
90pub struct InspectCandidate<'a, 'tcx> {
91 goal: &'a InspectGoal<'a, 'tcx>,
92 kind: inspect::ProbeKind<TyCtxt<'tcx>>,
93 steps: Vec<&'a inspect::ProbeStep<TyCtxt<'tcx>>>,
94 final_state: inspect::CanonicalState<TyCtxt<'tcx>, ()>,
95 result: QueryResult<'tcx>,
96 shallow_certainty: Certainty,
97}
98
99impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
100 pub fn kind(&self) -> inspect::ProbeKind<TyCtxt<'tcx>> {
101 self.kind
102 }
103
104 pub fn result(&self) -> Result<Certainty, NoSolution> {
105 self.result.map(|c| c.value.certainty)
106 }
107
108 pub fn goal(&self) -> &'a InspectGoal<'a, 'tcx> {
109 self.goal
110 }
111
112 pub fn shallow_certainty(&self) -> Certainty {
121 self.shallow_certainty
122 }
123
124 pub fn visit_nested_no_probe<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {
128 for goal in self.instantiate_nested_goals(visitor.span()) {
129 try_visit!(goal.visit_with(visitor));
130 }
131
132 V::Result::output()
133 }
134
135 #[instrument(
140 level = "debug",
141 skip_all,
142 fields(goal = ?self.goal.goal, steps = ?self.steps)
143 )]
144 pub fn instantiate_nested_goals(&self, span: Span) -> Vec<InspectGoal<'a, 'tcx>> {
145 let infcx = self.goal.infcx;
146 let param_env = self.goal.goal.param_env;
147 let mut orig_values = self.goal.orig_values.to_vec();
148
149 let mut instantiated_goals = vec![];
150 for step in &self.steps {
151 match **step {
152 inspect::ProbeStep::AddGoal(source, goal) => instantiated_goals.push((
153 source,
154 instantiate_canonical_state(infcx, span, param_env, &mut orig_values, goal),
155 )),
156 inspect::ProbeStep::RecordImplArgs { .. } => {}
157 inspect::ProbeStep::MakeCanonicalResponse { .. }
158 | inspect::ProbeStep::NestedProbe(_) => unreachable!(),
159 }
160 }
161
162 let () =
163 instantiate_canonical_state(infcx, span, param_env, &mut orig_values, self.final_state);
164
165 if let Some(term_hack) = &self.goal.normalizes_to_term_hack {
166 let _ = term_hack.constrain_and(infcx, span, param_env, |_| {});
170 }
171
172 instantiated_goals
173 .into_iter()
174 .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span))
175 .collect()
176 }
177
178 #[instrument(
182 level = "debug",
183 skip_all,
184 fields(goal = ?self.goal.goal, steps = ?self.steps)
185 )]
186 pub fn instantiate_impl_args(&self, span: Span) -> ty::GenericArgsRef<'tcx> {
187 let infcx = self.goal.infcx;
188 let param_env = self.goal.goal.param_env;
189 let mut orig_values = self.goal.orig_values.to_vec();
190
191 for step in &self.steps {
192 match **step {
193 inspect::ProbeStep::RecordImplArgs { impl_args } => {
194 let impl_args = instantiate_canonical_state(
195 infcx,
196 span,
197 param_env,
198 &mut orig_values,
199 impl_args,
200 );
201
202 let () = instantiate_canonical_state(
203 infcx,
204 span,
205 param_env,
206 &mut orig_values,
207 self.final_state,
208 );
209
210 assert!(
212 self.goal.normalizes_to_term_hack.is_none(),
213 "cannot use `instantiate_impl_args` with a `NormalizesTo` goal"
214 );
215
216 return eager_resolve_vars(infcx, impl_args);
217 }
218 inspect::ProbeStep::AddGoal(..) => {}
219 inspect::ProbeStep::MakeCanonicalResponse { .. }
220 | inspect::ProbeStep::NestedProbe(_) => unreachable!(),
221 }
222 }
223
224 bug!("expected impl args probe step for `instantiate_impl_args`");
225 }
226
227 pub fn instantiate_proof_tree_for_nested_goal(
228 &self,
229 source: GoalSource,
230 goal: Goal<'tcx, ty::Predicate<'tcx>>,
231 span: Span,
232 ) -> InspectGoal<'a, 'tcx> {
233 let infcx = self.goal.infcx;
234 match goal.predicate.kind().no_bound_vars() {
235 Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
236 let unconstrained_term = infcx.next_term_var_of_kind(term, span);
237 let goal =
238 goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
239 let normalizes_to_term_hack = NormalizesToTermHack { term, unconstrained_term };
246 let (proof_tree, nested_goals_result) = infcx.probe(|_| {
247 let (nested, proof_tree) =
252 infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes, None);
253 let proof_tree = proof_tree.unwrap();
254 let nested_goals_result = nested.and_then(|(nested, _)| {
255 normalizes_to_term_hack.constrain_and(
256 infcx,
257 span,
258 proof_tree.uncanonicalized_goal.param_env,
259 |ocx| {
260 ocx.register_obligations(nested.0.into_iter().map(|(_, goal)| {
261 Obligation::new(
262 infcx.tcx,
263 ObligationCause::dummy_with_span(span),
264 goal.param_env,
265 goal.predicate,
266 )
267 }));
268 },
269 )
270 });
271 (proof_tree, nested_goals_result)
272 });
273 InspectGoal::new(
274 infcx,
275 self.goal.depth + 1,
276 proof_tree,
277 Some((normalizes_to_term_hack, nested_goals_result)),
278 source,
279 )
280 }
281 _ => {
282 let proof_tree = infcx
288 .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes, span, None).1)
289 .unwrap();
290 InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
291 }
292 }
293 }
294
295 pub fn visit_nested_in_probe<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {
298 self.goal.infcx.probe(|_| self.visit_nested_no_probe(visitor))
299 }
300}
301
302impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
303 pub fn infcx(&self) -> &'a InferCtxt<'tcx> {
304 self.infcx
305 }
306
307 pub fn goal(&self) -> Goal<'tcx, ty::Predicate<'tcx>> {
308 self.goal
309 }
310
311 pub fn result(&self) -> Result<Certainty, NoSolution> {
312 self.result
313 }
314
315 pub fn source(&self) -> GoalSource {
316 self.source
317 }
318
319 pub fn depth(&self) -> usize {
320 self.depth
321 }
322
323 fn candidates_recur(
324 &'a self,
325 candidates: &mut Vec<InspectCandidate<'a, 'tcx>>,
326 steps: &mut Vec<&'a inspect::ProbeStep<TyCtxt<'tcx>>>,
327 probe: &'a inspect::Probe<TyCtxt<'tcx>>,
328 ) {
329 let mut shallow_certainty = None;
330 for step in &probe.steps {
331 match *step {
332 inspect::ProbeStep::AddGoal(..) | inspect::ProbeStep::RecordImplArgs { .. } => {
333 steps.push(step)
334 }
335 inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => {
336 assert_matches!(
337 shallow_certainty.replace(c),
338 None | Some(Certainty::Maybe(MaybeCause::Ambiguity))
339 );
340 }
341 inspect::ProbeStep::NestedProbe(ref probe) => {
342 match probe.kind {
343 inspect::ProbeKind::ProjectionCompatibility
345 | inspect::ProbeKind::ShadowedEnvProbing => continue,
346
347 inspect::ProbeKind::NormalizedSelfTyAssembly
348 | inspect::ProbeKind::UnsizeAssembly
349 | inspect::ProbeKind::Root { .. }
350 | inspect::ProbeKind::TraitCandidate { .. }
351 | inspect::ProbeKind::OpaqueTypeStorageLookup { .. }
352 | inspect::ProbeKind::RigidAlias { .. } => {
353 let num_steps = steps.len();
357 self.candidates_recur(candidates, steps, probe);
358 steps.truncate(num_steps);
359 }
360 }
361 }
362 }
363 }
364
365 match probe.kind {
366 inspect::ProbeKind::ProjectionCompatibility
367 | inspect::ProbeKind::ShadowedEnvProbing => {
368 bug!()
369 }
370
371 inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly => {}
372
373 inspect::ProbeKind::Root { result }
376 | inspect::ProbeKind::TraitCandidate { source: _, result }
377 | inspect::ProbeKind::OpaqueTypeStorageLookup { result }
378 | inspect::ProbeKind::RigidAlias { result } => {
379 if let Some(shallow_certainty) = shallow_certainty {
382 candidates.push(InspectCandidate {
383 goal: self,
384 kind: probe.kind,
385 steps: steps.clone(),
386 final_state: probe.final_state,
387 shallow_certainty,
388 result,
389 });
390 }
391 }
392 }
393 }
394
395 pub fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'tcx>> {
396 let mut candidates = vec![];
397 let last_eval_step = match &self.evaluation_kind {
398 inspect::CanonicalGoalEvaluationKind::Overflow => return vec![],
400 inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision } => final_revision,
401 };
402
403 let mut nested_goals = vec![];
404 self.candidates_recur(&mut candidates, &mut nested_goals, &last_eval_step);
405
406 candidates
407 }
408
409 pub fn unique_applicable_candidate(&'a self) -> Option<InspectCandidate<'a, 'tcx>> {
413 let mut candidates = self.candidates();
416 candidates.retain(|c| c.result().is_ok());
417 candidates.pop().filter(|_| candidates.is_empty())
418 }
419
420 fn new(
421 infcx: &'a InferCtxt<'tcx>,
422 depth: usize,
423 root: inspect::GoalEvaluation<TyCtxt<'tcx>>,
424 term_hack_and_nested_certainty: Option<(
425 NormalizesToTermHack<'tcx>,
426 Result<Certainty, NoSolution>,
427 )>,
428 source: GoalSource,
429 ) -> Self {
430 let infcx = <&SolverDelegate<'tcx>>::from(infcx);
431
432 let inspect::GoalEvaluation { uncanonicalized_goal, orig_values, evaluation } = root;
433 let result = evaluation.result.and_then(|ok| {
436 let nested_goals_certainty =
437 term_hack_and_nested_certainty.map_or(Ok(Certainty::Yes), |(_, c)| c)?;
438 Ok(ok.value.certainty.and(nested_goals_certainty))
439 });
440
441 InspectGoal {
442 infcx,
443 depth,
444 orig_values,
445 goal: eager_resolve_vars(infcx, uncanonicalized_goal),
446 result,
447 evaluation_kind: evaluation.kind,
448 normalizes_to_term_hack: term_hack_and_nested_certainty.map(|(n, _)| n),
449 source,
450 }
451 }
452
453 pub(crate) fn visit_with<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {
454 if self.depth < visitor.config().max_depth {
455 try_visit!(visitor.visit_goal(self));
456 }
457
458 V::Result::output()
459 }
460}
461
462pub trait ProofTreeVisitor<'tcx> {
464 type Result: VisitorResult = ();
465
466 fn span(&self) -> Span;
467
468 fn config(&self) -> InspectConfig {
469 InspectConfig { max_depth: 10 }
470 }
471
472 fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> Self::Result;
473}
474
475#[extension(pub trait ProofTreeInferCtxtExt<'tcx>)]
476impl<'tcx> InferCtxt<'tcx> {
477 fn visit_proof_tree<V: ProofTreeVisitor<'tcx>>(
478 &self,
479 goal: Goal<'tcx, ty::Predicate<'tcx>>,
480 visitor: &mut V,
481 ) -> V::Result {
482 self.visit_proof_tree_at_depth(goal, 0, visitor)
483 }
484
485 fn visit_proof_tree_at_depth<V: ProofTreeVisitor<'tcx>>(
486 &self,
487 goal: Goal<'tcx, ty::Predicate<'tcx>>,
488 depth: usize,
489 visitor: &mut V,
490 ) -> V::Result {
491 let (_, proof_tree) = <&SolverDelegate<'tcx>>::from(self).evaluate_root_goal(
492 goal,
493 GenerateProofTree::Yes,
494 visitor.span(),
495 None,
496 );
497 let proof_tree = proof_tree.unwrap();
498 visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc))
499 }
500}