rustc_trait_selection/solve/inspect/
analyse.rs
1use std::assert_matches::assert_matches;
13
14use rustc_ast_ir::try_visit;
15use rustc_ast_ir::visit::VisitorResult;
16use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
17use rustc_macros::extension;
18use rustc_middle::traits::ObligationCause;
19use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, NoSolution, QueryResult};
20use rustc_middle::ty::{TyCtxt, TypeFoldable};
21use rustc_middle::{bug, ty};
22use rustc_next_trait_solver::resolve::EagerResolver;
23use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state};
24use rustc_next_trait_solver::solve::{GenerateProofTree, MaybeCause, SolverDelegateEvalExt as _};
25use rustc_span::{DUMMY_SP, Span};
26use tracing::instrument;
27
28use crate::solve::delegate::SolverDelegate;
29use crate::traits::ObligationCtxt;
30
31pub struct InspectConfig {
32 pub max_depth: usize,
33}
34
35pub struct InspectGoal<'a, 'tcx> {
36 infcx: &'a SolverDelegate<'tcx>,
37 depth: usize,
38 orig_values: Vec<ty::GenericArg<'tcx>>,
39 goal: Goal<'tcx, ty::Predicate<'tcx>>,
40 result: Result<Certainty, NoSolution>,
41 evaluation_kind: inspect::CanonicalGoalEvaluationKind<TyCtxt<'tcx>>,
42 normalizes_to_term_hack: Option<NormalizesToTermHack<'tcx>>,
43 source: GoalSource,
44}
45
46#[derive(Copy, Clone)]
56struct NormalizesToTermHack<'tcx> {
57 term: ty::Term<'tcx>,
58 unconstrained_term: ty::Term<'tcx>,
59}
60
61impl<'tcx> NormalizesToTermHack<'tcx> {
62 fn constrain(
66 self,
67 infcx: &InferCtxt<'tcx>,
68 span: Span,
69 param_env: ty::ParamEnv<'tcx>,
70 ) -> Result<Certainty, NoSolution> {
71 infcx
72 .at(&ObligationCause::dummy_with_span(span), param_env)
73 .eq(DefineOpaqueTypes::Yes, self.term, self.unconstrained_term)
74 .map_err(|_| NoSolution)
75 .and_then(|InferOk { value: (), obligations }| {
76 let ocx = ObligationCtxt::new(infcx);
77 ocx.register_obligations(obligations);
78 let errors = ocx.select_all_or_error();
79 if errors.is_empty() {
80 Ok(Certainty::Yes)
81 } else if errors.iter().all(|e| !e.is_true_error()) {
82 Ok(Certainty::AMBIGUOUS)
83 } else {
84 Err(NoSolution)
85 }
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 pub fn instantiate_nested_goals(&self, span: Span) -> Vec<InspectGoal<'a, 'tcx>> {
140 self.instantiate_nested_goals_and_opt_impl_args(span).0
141 }
142
143 #[instrument(
148 level = "debug",
149 skip_all,
150 fields(goal = ?self.goal.goal, steps = ?self.steps)
151 )]
152 pub fn instantiate_nested_goals_and_opt_impl_args(
153 &self,
154 span: Span,
155 ) -> (Vec<InspectGoal<'a, 'tcx>>, Option<ty::GenericArgsRef<'tcx>>) {
156 let infcx = self.goal.infcx;
157 let param_env = self.goal.goal.param_env;
158 let mut orig_values = self.goal.orig_values.to_vec();
159
160 let mut instantiated_goals = vec![];
161 let mut opt_impl_args = None;
162 for step in &self.steps {
163 match **step {
164 inspect::ProbeStep::AddGoal(source, goal) => instantiated_goals.push((
165 source,
166 instantiate_canonical_state(infcx, span, param_env, &mut orig_values, goal),
167 )),
168 inspect::ProbeStep::RecordImplArgs { impl_args } => {
169 opt_impl_args = Some(instantiate_canonical_state(
170 infcx,
171 span,
172 param_env,
173 &mut orig_values,
174 impl_args,
175 ));
176 }
177 inspect::ProbeStep::MakeCanonicalResponse { .. }
178 | inspect::ProbeStep::NestedProbe(_) => unreachable!(),
179 }
180 }
181
182 let () =
183 instantiate_canonical_state(infcx, span, param_env, &mut orig_values, self.final_state);
184
185 if let Some(term_hack) = self.goal.normalizes_to_term_hack {
186 let _ = term_hack.constrain(infcx, span, param_env);
190 }
191
192 let opt_impl_args =
193 opt_impl_args.map(|impl_args| impl_args.fold_with(&mut EagerResolver::new(infcx)));
194
195 let goals = instantiated_goals
196 .into_iter()
197 .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span))
198 .collect();
199
200 (goals, opt_impl_args)
201 }
202
203 pub fn instantiate_proof_tree_for_nested_goal(
204 &self,
205 source: GoalSource,
206 goal: Goal<'tcx, ty::Predicate<'tcx>>,
207 span: Span,
208 ) -> InspectGoal<'a, 'tcx> {
209 let infcx = self.goal.infcx;
210 match goal.predicate.kind().no_bound_vars() {
211 Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
212 let unconstrained_term = match term.unpack() {
213 ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(),
214 ty::TermKind::Const(_) => infcx.next_const_var(span).into(),
215 };
216 let goal =
217 goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
218 let proof_tree =
225 infcx.probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1);
226 InspectGoal::new(
227 infcx,
228 self.goal.depth + 1,
229 proof_tree.unwrap(),
230 Some(NormalizesToTermHack { term, unconstrained_term }),
231 source,
232 )
233 }
234 _ => {
235 let proof_tree = infcx
241 .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes, span).1)
242 .unwrap();
243 InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
244 }
245 }
246 }
247
248 pub fn visit_nested_in_probe<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {
251 self.goal.infcx.probe(|_| self.visit_nested_no_probe(visitor))
252 }
253}
254
255impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
256 pub fn infcx(&self) -> &'a InferCtxt<'tcx> {
257 self.infcx
258 }
259
260 pub fn goal(&self) -> Goal<'tcx, ty::Predicate<'tcx>> {
261 self.goal
262 }
263
264 pub fn result(&self) -> Result<Certainty, NoSolution> {
265 self.result
266 }
267
268 pub fn source(&self) -> GoalSource {
269 self.source
270 }
271
272 pub fn depth(&self) -> usize {
273 self.depth
274 }
275
276 fn candidates_recur(
277 &'a self,
278 candidates: &mut Vec<InspectCandidate<'a, 'tcx>>,
279 steps: &mut Vec<&'a inspect::ProbeStep<TyCtxt<'tcx>>>,
280 probe: &'a inspect::Probe<TyCtxt<'tcx>>,
281 ) {
282 let mut shallow_certainty = None;
283 for step in &probe.steps {
284 match *step {
285 inspect::ProbeStep::AddGoal(..) | inspect::ProbeStep::RecordImplArgs { .. } => {
286 steps.push(step)
287 }
288 inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => {
289 assert_matches!(
290 shallow_certainty.replace(c),
291 None | Some(Certainty::Maybe(MaybeCause::Ambiguity))
292 );
293 }
294 inspect::ProbeStep::NestedProbe(ref probe) => {
295 match probe.kind {
296 inspect::ProbeKind::UpcastProjectionCompatibility
298 | inspect::ProbeKind::ShadowedEnvProbing => continue,
299
300 inspect::ProbeKind::NormalizedSelfTyAssembly
301 | inspect::ProbeKind::UnsizeAssembly
302 | inspect::ProbeKind::Root { .. }
303 | inspect::ProbeKind::TraitCandidate { .. }
304 | inspect::ProbeKind::OpaqueTypeStorageLookup { .. }
305 | inspect::ProbeKind::RigidAlias { .. } => {
306 let num_steps = steps.len();
310 self.candidates_recur(candidates, steps, probe);
311 steps.truncate(num_steps);
312 }
313 }
314 }
315 }
316 }
317
318 match probe.kind {
319 inspect::ProbeKind::UpcastProjectionCompatibility
320 | inspect::ProbeKind::ShadowedEnvProbing => bug!(),
321
322 inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly => {}
323
324 inspect::ProbeKind::Root { result }
327 | inspect::ProbeKind::TraitCandidate { source: _, result }
328 | inspect::ProbeKind::OpaqueTypeStorageLookup { result }
329 | inspect::ProbeKind::RigidAlias { result } => {
330 if let Some(shallow_certainty) = shallow_certainty {
333 candidates.push(InspectCandidate {
334 goal: self,
335 kind: probe.kind,
336 steps: steps.clone(),
337 final_state: probe.final_state,
338 shallow_certainty,
339 result,
340 });
341 }
342 }
343 }
344 }
345
346 pub fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'tcx>> {
347 let mut candidates = vec![];
348 let last_eval_step = match &self.evaluation_kind {
349 inspect::CanonicalGoalEvaluationKind::Overflow => return vec![],
351 inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision } => final_revision,
352 };
353
354 let mut nested_goals = vec![];
355 self.candidates_recur(&mut candidates, &mut nested_goals, &last_eval_step);
356
357 candidates
358 }
359
360 pub fn unique_applicable_candidate(&'a self) -> Option<InspectCandidate<'a, 'tcx>> {
364 let mut candidates = self.candidates();
367 candidates.retain(|c| c.result().is_ok());
368 candidates.pop().filter(|_| candidates.is_empty())
369 }
370
371 fn new(
372 infcx: &'a InferCtxt<'tcx>,
373 depth: usize,
374 root: inspect::GoalEvaluation<TyCtxt<'tcx>>,
375 normalizes_to_term_hack: Option<NormalizesToTermHack<'tcx>>,
376 source: GoalSource,
377 ) -> Self {
378 let infcx = <&SolverDelegate<'tcx>>::from(infcx);
379
380 let inspect::GoalEvaluation { uncanonicalized_goal, orig_values, evaluation } = root;
381 let result = evaluation.result.and_then(|ok| {
382 if let Some(term_hack) = normalizes_to_term_hack {
383 infcx
384 .probe(|_| term_hack.constrain(infcx, DUMMY_SP, uncanonicalized_goal.param_env))
385 .map(|certainty| ok.value.certainty.unify_with(certainty))
386 } else {
387 Ok(ok.value.certainty)
388 }
389 });
390
391 InspectGoal {
392 infcx,
393 depth,
394 orig_values,
395 goal: uncanonicalized_goal.fold_with(&mut EagerResolver::new(infcx)),
396 result,
397 evaluation_kind: evaluation.kind,
398 normalizes_to_term_hack,
399 source,
400 }
401 }
402
403 pub(crate) fn visit_with<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {
404 if self.depth < visitor.config().max_depth {
405 try_visit!(visitor.visit_goal(self));
406 }
407
408 V::Result::output()
409 }
410}
411
412pub trait ProofTreeVisitor<'tcx> {
414 type Result: VisitorResult = ();
415
416 fn span(&self) -> Span;
417
418 fn config(&self) -> InspectConfig {
419 InspectConfig { max_depth: 10 }
420 }
421
422 fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> Self::Result;
423}
424
425#[extension(pub trait ProofTreeInferCtxtExt<'tcx>)]
426impl<'tcx> InferCtxt<'tcx> {
427 fn visit_proof_tree<V: ProofTreeVisitor<'tcx>>(
428 &self,
429 goal: Goal<'tcx, ty::Predicate<'tcx>>,
430 visitor: &mut V,
431 ) -> V::Result {
432 self.visit_proof_tree_at_depth(goal, 0, visitor)
433 }
434
435 fn visit_proof_tree_at_depth<V: ProofTreeVisitor<'tcx>>(
436 &self,
437 goal: Goal<'tcx, ty::Predicate<'tcx>>,
438 depth: usize,
439 visitor: &mut V,
440 ) -> V::Result {
441 let (_, proof_tree) = <&SolverDelegate<'tcx>>::from(self).evaluate_root_goal(
442 goal,
443 GenerateProofTree::Yes,
444 visitor.span(),
445 );
446 let proof_tree = proof_tree.unwrap();
447 visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc))
448 }
449}