1use std::ops::ControlFlow;
2
3use rustc_hir::LangItem;
4use rustc_infer::infer::InferCtxt;
5use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause};
6use rustc_infer::traits::{
7 self, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode,
8 PredicateObligation, SelectionError,
9};
10use rustc_middle::traits::query::NoSolution;
11use rustc_middle::ty::error::{ExpectedFound, TypeError};
12use rustc_middle::ty::{self, Ty, TyCtxt};
13use rustc_middle::{bug, span_bug};
14use rustc_next_trait_solver::solve::{
15 GenerateProofTree, GoalEvaluation, SolverDelegateEvalExt as _,
16};
17use tracing::{instrument, trace};
18
19use crate::solve::delegate::SolverDelegate;
20use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
21use crate::solve::{Certainty, deeply_normalize_for_diagnostics};
22use crate::traits::{FulfillmentError, FulfillmentErrorCode, wf};
23
24pub(super) fn fulfillment_error_for_no_solution<'tcx>(
25 infcx: &InferCtxt<'tcx>,
26 root_obligation: PredicateObligation<'tcx>,
27) -> FulfillmentError<'tcx> {
28 let obligation = find_best_leaf_obligation(infcx, &root_obligation, false);
29
30 let code = match obligation.predicate.kind().skip_binder() {
31 ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
32 FulfillmentErrorCode::Project(
33 MismatchedProjectionTypes { err: TypeError::Mismatch },
35 )
36 }
37 ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => {
38 let ct_ty = match ct.kind() {
39 ty::ConstKind::Unevaluated(uv) => {
40 infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
41 }
42 ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env),
43 ty::ConstKind::Value(cv) => cv.ty,
44 kind => span_bug!(
45 obligation.cause.span,
46 "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
47 ),
48 };
49 FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType {
50 ct,
51 ct_ty,
52 expected_ty,
53 })
54 }
55 ty::PredicateKind::NormalizesTo(..) => {
56 FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
57 }
58 ty::PredicateKind::AliasRelate(_, _, _) => {
59 FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
60 }
61 ty::PredicateKind::Subtype(pred) => {
62 let (a, b) = infcx.enter_forall_and_leak_universe(
63 obligation.predicate.kind().rebind((pred.a, pred.b)),
64 );
65 let expected_found = ExpectedFound::new(a, b);
66 FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
67 }
68 ty::PredicateKind::Coerce(pred) => {
69 let (a, b) = infcx.enter_forall_and_leak_universe(
70 obligation.predicate.kind().rebind((pred.a, pred.b)),
71 );
72 let expected_found = ExpectedFound::new(b, a);
73 FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
74 }
75 ty::PredicateKind::Clause(_)
76 | ty::PredicateKind::DynCompatible(_)
77 | ty::PredicateKind::Ambiguous => {
78 FulfillmentErrorCode::Select(SelectionError::Unimplemented)
79 }
80 ty::PredicateKind::ConstEquate(..) => {
81 bug!("unexpected goal: {obligation:?}")
82 }
83 };
84
85 FulfillmentError { obligation, code, root_obligation }
86}
87
88pub(super) fn fulfillment_error_for_stalled<'tcx>(
89 infcx: &InferCtxt<'tcx>,
90 root_obligation: PredicateObligation<'tcx>,
91) -> FulfillmentError<'tcx> {
92 let (code, refine_obligation) = infcx.probe(|_| {
93 match <&SolverDelegate<'tcx>>::from(infcx)
94 .evaluate_root_goal(
95 root_obligation.as_goal(),
96 GenerateProofTree::No,
97 root_obligation.cause.span,
98 None,
99 )
100 .0
101 {
102 Ok(GoalEvaluation { certainty: Certainty::Maybe(MaybeCause::Ambiguity), .. }) => {
103 (FulfillmentErrorCode::Ambiguity { overflow: None }, true)
104 }
105 Ok(GoalEvaluation {
106 certainty:
107 Certainty::Maybe(MaybeCause::Overflow {
108 suggest_increasing_limit,
109 keep_constraints: _,
110 }),
111 ..
112 }) => (
113 FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
114 false,
121 ),
122 Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => {
123 span_bug!(
124 root_obligation.cause.span,
125 "did not expect successful goal when collecting ambiguity errors for `{:?}`",
126 infcx.resolve_vars_if_possible(root_obligation.predicate),
127 )
128 }
129 Err(_) => {
130 span_bug!(
131 root_obligation.cause.span,
132 "did not expect selection error when collecting ambiguity errors for `{:?}`",
133 infcx.resolve_vars_if_possible(root_obligation.predicate),
134 )
135 }
136 }
137 });
138
139 FulfillmentError {
140 obligation: if refine_obligation {
141 find_best_leaf_obligation(infcx, &root_obligation, true)
142 } else {
143 root_obligation.clone()
144 },
145 code,
146 root_obligation,
147 }
148}
149
150pub(super) fn fulfillment_error_for_overflow<'tcx>(
151 infcx: &InferCtxt<'tcx>,
152 root_obligation: PredicateObligation<'tcx>,
153) -> FulfillmentError<'tcx> {
154 FulfillmentError {
155 obligation: find_best_leaf_obligation(infcx, &root_obligation, true),
156 code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
157 root_obligation,
158 }
159}
160
161#[instrument(level = "debug", skip(infcx), ret)]
162fn find_best_leaf_obligation<'tcx>(
163 infcx: &InferCtxt<'tcx>,
164 obligation: &PredicateObligation<'tcx>,
165 consider_ambiguities: bool,
166) -> PredicateObligation<'tcx> {
167 let obligation = infcx.resolve_vars_if_possible(obligation.clone());
168 let obligation = infcx
174 .fudge_inference_if_ok(|| {
175 infcx
176 .visit_proof_tree(
177 obligation.as_goal(),
178 &mut BestObligation { obligation: obligation.clone(), consider_ambiguities },
179 )
180 .break_value()
181 .ok_or(())
182 })
183 .unwrap_or(obligation);
184 deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation)
185}
186
187struct BestObligation<'tcx> {
188 obligation: PredicateObligation<'tcx>,
189 consider_ambiguities: bool,
190}
191
192impl<'tcx> BestObligation<'tcx> {
193 fn with_derived_obligation(
194 &mut self,
195 derived_obligation: PredicateObligation<'tcx>,
196 and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'tcx>>::Result,
197 ) -> <Self as ProofTreeVisitor<'tcx>>::Result {
198 let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
199 let res = and_then(self);
200 self.obligation = old_obligation;
201 res
202 }
203
204 fn non_trivial_candidates<'a>(
209 &self,
210 goal: &'a inspect::InspectGoal<'a, 'tcx>,
211 ) -> Vec<inspect::InspectCandidate<'a, 'tcx>> {
212 let mut candidates = goal.candidates();
213 match self.consider_ambiguities {
214 true => {
215 candidates.retain(|candidate| candidate.result().is_ok());
219 }
220 false => {
221 candidates.retain(|c| !matches!(c.kind(), inspect::ProbeKind::RigidAlias { .. }));
224 if candidates.len() > 1 {
228 candidates.retain(|candidate| {
229 goal.infcx().probe(|_| {
230 candidate.instantiate_nested_goals(self.span()).iter().any(
231 |nested_goal| {
232 matches!(
233 nested_goal.source(),
234 GoalSource::ImplWhereBound
235 | GoalSource::AliasBoundConstCondition
236 | GoalSource::InstantiateHigherRanked
237 | GoalSource::AliasWellFormed
238 ) && nested_goal.result().is_err()
239 },
240 )
241 })
242 });
243 }
244 }
245 }
246
247 candidates
248 }
249
250 fn visit_well_formed_goal(
254 &mut self,
255 candidate: &inspect::InspectCandidate<'_, 'tcx>,
256 term: ty::Term<'tcx>,
257 ) -> ControlFlow<PredicateObligation<'tcx>> {
258 let infcx = candidate.goal().infcx();
259 let param_env = candidate.goal().goal().param_env;
260 let body_id = self.obligation.cause.body_id;
261
262 for obligation in wf::unnormalized_obligations(infcx, param_env, term, self.span(), body_id)
263 .into_iter()
264 .flatten()
265 {
266 let nested_goal = candidate.instantiate_proof_tree_for_nested_goal(
267 GoalSource::Misc,
268 obligation.as_goal(),
269 self.span(),
270 );
271 match (self.consider_ambiguities, nested_goal.result()) {
273 (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
274 _ => continue,
275 }
276
277 self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
278 }
279
280 ControlFlow::Break(self.obligation.clone())
281 }
282
283 fn detect_error_in_self_ty_normalization(
287 &mut self,
288 goal: &inspect::InspectGoal<'_, 'tcx>,
289 self_ty: Ty<'tcx>,
290 ) -> ControlFlow<PredicateObligation<'tcx>> {
291 assert!(!self.consider_ambiguities);
292 let tcx = goal.infcx().tcx;
293 if let ty::Alias(..) = self_ty.kind() {
294 let infer_term = goal.infcx().next_ty_var(self.obligation.cause.span);
295 let pred = ty::PredicateKind::AliasRelate(
296 self_ty.into(),
297 infer_term.into(),
298 ty::AliasRelationDirection::Equate,
299 );
300 let obligation =
301 Obligation::new(tcx, self.obligation.cause.clone(), goal.goal().param_env, pred);
302 self.with_derived_obligation(obligation, |this| {
303 goal.infcx().visit_proof_tree_at_depth(
304 goal.goal().with(tcx, pred),
305 goal.depth() + 1,
306 this,
307 )
308 })
309 } else {
310 ControlFlow::Continue(())
311 }
312 }
313
314 fn detect_trait_error_in_higher_ranked_projection(
322 &mut self,
323 goal: &inspect::InspectGoal<'_, 'tcx>,
324 ) -> ControlFlow<PredicateObligation<'tcx>> {
325 let tcx = goal.infcx().tcx;
326 if let Some(projection_clause) = goal.goal().predicate.as_projection_clause()
327 && !projection_clause.bound_vars().is_empty()
328 {
329 let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(tcx));
330 let obligation = Obligation::new(
331 tcx,
332 self.obligation.cause.clone(),
333 goal.goal().param_env,
334 deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred),
335 );
336 self.with_derived_obligation(obligation, |this| {
337 goal.infcx().visit_proof_tree_at_depth(
338 goal.goal().with(tcx, pred),
339 goal.depth() + 1,
340 this,
341 )
342 })
343 } else {
344 ControlFlow::Continue(())
345 }
346 }
347
348 fn detect_non_well_formed_assoc_item(
355 &mut self,
356 goal: &inspect::InspectGoal<'_, 'tcx>,
357 alias: ty::AliasTerm<'tcx>,
358 ) -> ControlFlow<PredicateObligation<'tcx>> {
359 let tcx = goal.infcx().tcx;
360 let obligation = Obligation::new(
361 tcx,
362 self.obligation.cause.clone(),
363 goal.goal().param_env,
364 alias.trait_ref(tcx),
365 );
366 self.with_derived_obligation(obligation, |this| {
367 goal.infcx().visit_proof_tree_at_depth(
368 goal.goal().with(tcx, alias.trait_ref(tcx)),
369 goal.depth() + 1,
370 this,
371 )
372 })
373 }
374
375 fn detect_error_from_empty_candidates(
378 &mut self,
379 goal: &inspect::InspectGoal<'_, 'tcx>,
380 ) -> ControlFlow<PredicateObligation<'tcx>> {
381 let tcx = goal.infcx().tcx;
382 let pred_kind = goal.goal().predicate.kind();
383
384 match pred_kind.no_bound_vars() {
385 Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))) => {
386 self.detect_error_in_self_ty_normalization(goal, pred.self_ty())?;
387 }
388 Some(ty::PredicateKind::NormalizesTo(pred))
389 if let ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst =
390 pred.alias.kind(tcx) =>
391 {
392 self.detect_error_in_self_ty_normalization(goal, pred.alias.self_ty())?;
393 self.detect_non_well_formed_assoc_item(goal, pred.alias)?;
394 }
395 Some(_) | None => {}
396 }
397
398 ControlFlow::Break(self.obligation.clone())
399 }
400}
401
402impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
403 type Result = ControlFlow<PredicateObligation<'tcx>>;
404
405 fn span(&self) -> rustc_span::Span {
406 self.obligation.cause.span
407 }
408
409 #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
410 fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
411 let tcx = goal.infcx().tcx;
412 match (self.consider_ambiguities, goal.result()) {
414 (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
415 _ => return ControlFlow::Continue(()),
416 }
417
418 let pred = goal.goal().predicate;
419
420 let candidates = self.non_trivial_candidates(goal);
421 let candidate = match candidates.as_slice() {
422 [candidate] => candidate,
423 [] => return self.detect_error_from_empty_candidates(goal),
424 _ => return ControlFlow::Break(self.obligation.clone()),
425 };
426
427 if let inspect::ProbeKind::TraitCandidate {
429 source: CandidateSource::Impl(impl_def_id),
430 result: _,
431 } = candidate.kind()
432 && tcx.do_not_recommend_impl(impl_def_id)
433 {
434 trace!("#[do_not_recommend] -> exit");
435 return ControlFlow::Break(self.obligation.clone());
436 }
437
438 let child_mode = match pred.kind().skip_binder() {
441 ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {
442 ChildMode::Trait(pred.kind().rebind(trait_pred))
443 }
444 ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(host_pred)) => {
445 ChildMode::Host(pred.kind().rebind(host_pred))
446 }
447 ty::PredicateKind::NormalizesTo(normalizes_to)
448 if matches!(
449 normalizes_to.alias.kind(tcx),
450 ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst
451 ) =>
452 {
453 ChildMode::Trait(pred.kind().rebind(ty::TraitPredicate {
454 trait_ref: normalizes_to.alias.trait_ref(tcx),
455 polarity: ty::PredicatePolarity::Positive,
456 }))
457 }
458 ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => {
459 return self.visit_well_formed_goal(candidate, term);
460 }
461 _ => ChildMode::PassThrough,
462 };
463
464 let nested_goals = candidate.instantiate_nested_goals(self.span());
465
466 for nested_goal in &nested_goals {
474 if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
475 && tcx.is_lang_item(poly_trait_pred.def_id(), LangItem::FnPtrTrait)
476 && let Err(NoSolution) = nested_goal.result()
477 {
478 return ControlFlow::Break(self.obligation.clone());
479 }
480 }
481
482 let mut impl_where_bound_count = 0;
483 for nested_goal in nested_goals {
484 trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
485
486 let nested_pred = nested_goal.goal().predicate;
487
488 let make_obligation = |cause| Obligation {
489 cause,
490 param_env: nested_goal.goal().param_env,
491 predicate: nested_pred,
492 recursion_depth: self.obligation.recursion_depth + 1,
493 };
494
495 let obligation;
496 match (child_mode, nested_goal.source()) {
497 (
498 ChildMode::Trait(_) | ChildMode::Host(_),
499 GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_),
500 ) => {
501 continue;
502 }
503 (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
504 obligation = make_obligation(derive_cause(
505 tcx,
506 candidate.kind(),
507 self.obligation.cause.clone(),
508 impl_where_bound_count,
509 parent_trait_pred,
510 ));
511 impl_where_bound_count += 1;
512 }
513 (
514 ChildMode::Host(parent_host_pred),
515 GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
516 ) => {
517 obligation = make_obligation(derive_host_cause(
518 tcx,
519 candidate.kind(),
520 self.obligation.cause.clone(),
521 impl_where_bound_count,
522 parent_host_pred,
523 ));
524 impl_where_bound_count += 1;
525 }
526 (_, GoalSource::InstantiateHigherRanked) => {
528 obligation = self.obligation.clone();
529 }
530 (ChildMode::PassThrough, _)
531 | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
532 obligation = make_obligation(self.obligation.cause.clone());
533 }
534 }
535
536 self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
537 }
538
539 if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred.kind().no_bound_vars() {
542 goal.infcx().visit_proof_tree_at_depth(
543 goal.goal().with(tcx, ty::ClauseKind::WellFormed(lhs.into())),
544 goal.depth() + 1,
545 self,
546 )?;
547 goal.infcx().visit_proof_tree_at_depth(
548 goal.goal().with(tcx, ty::ClauseKind::WellFormed(rhs.into())),
549 goal.depth() + 1,
550 self,
551 )?;
552 }
553
554 self.detect_trait_error_in_higher_ranked_projection(goal)?;
555
556 ControlFlow::Break(self.obligation.clone())
557 }
558}
559
560#[derive(Debug, Copy, Clone)]
561enum ChildMode<'tcx> {
562 Trait(ty::PolyTraitPredicate<'tcx>),
566 Host(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>),
570 PassThrough,
574}
575
576fn derive_cause<'tcx>(
577 tcx: TyCtxt<'tcx>,
578 candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>,
579 mut cause: ObligationCause<'tcx>,
580 idx: usize,
581 parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
582) -> ObligationCause<'tcx> {
583 match candidate_kind {
584 inspect::ProbeKind::TraitCandidate {
585 source: CandidateSource::Impl(impl_def_id),
586 result: _,
587 } => {
588 if let Some((_, span)) =
589 tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx)
590 {
591 cause = cause.derived_cause(parent_trait_pred, |derived| {
592 ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause {
593 derived,
594 impl_or_alias_def_id: impl_def_id,
595 impl_def_predicate_index: Some(idx),
596 span,
597 }))
598 })
599 }
600 }
601 inspect::ProbeKind::TraitCandidate {
602 source: CandidateSource::BuiltinImpl(..),
603 result: _,
604 } => {
605 cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived);
606 }
607 _ => {}
608 };
609 cause
610}
611
612fn derive_host_cause<'tcx>(
613 tcx: TyCtxt<'tcx>,
614 candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>,
615 mut cause: ObligationCause<'tcx>,
616 idx: usize,
617 parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
618) -> ObligationCause<'tcx> {
619 match candidate_kind {
620 inspect::ProbeKind::TraitCandidate {
621 source: CandidateSource::Impl(impl_def_id),
622 result: _,
623 } => {
624 if let Some((_, span)) = tcx
625 .predicates_of(impl_def_id)
626 .instantiate_identity(tcx)
627 .into_iter()
628 .chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map(
629 |(trait_ref, span)| {
630 (
631 trait_ref.to_host_effect_clause(
632 tcx,
633 parent_host_pred.skip_binder().constness,
634 ),
635 span,
636 )
637 },
638 ))
639 .nth(idx)
640 {
641 cause =
642 cause.derived_host_cause(parent_host_pred, |derived| {
643 ObligationCauseCode::ImplDerivedHost(Box::new(
644 traits::ImplDerivedHostCause { derived, impl_def_id, span },
645 ))
646 })
647 }
648 }
649 inspect::ProbeKind::TraitCandidate {
650 source: CandidateSource::BuiltinImpl(..),
651 result: _,
652 } => {
653 cause =
654 cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost);
655 }
656 _ => {}
657 };
658 cause
659}