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