1use std::{fmt, ops::ControlFlow};
2
3use hir_def::attrs::AttrFlags;
4use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt};
5use rustc_type_ir::{
6 AliasRelationDirection, AliasTermKind, PredicatePolarity,
7 error::ExpectedFound,
8 inherent::IntoKind as _,
9 solve::{CandidateSource, Certainty, GoalSource, MaybeCause, MaybeInfo, NoSolution},
10};
11use tracing::{instrument, trace};
12
13use crate::{
14 Span,
15 db::GeneralConstId,
16 next_solver::{
17 AliasTerm, AnyImplId, Binder, ClauseKind, Const, ConstKind, DbInterner,
18 HostEffectPredicate, PolyTraitPredicate, Predicate, PredicateKind, SolverContext, Term,
19 TraitPredicate, Ty, TyKind, TypeError,
20 fulfill::NextSolverError,
21 infer::{
22 InferCtxt,
23 select::SelectionError,
24 traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations},
25 },
26 inspect::{self, ProofTreeVisitor},
27 normalize::deeply_normalize_for_diagnostics,
28 },
29};
30
31#[derive(Debug)]
32pub struct FulfillmentError<'db> {
33 pub obligation: PredicateObligation<'db>,
34 pub code: FulfillmentErrorCode<'db>,
35 pub parent_trait_obligations: Vec<Predicate<'db>>,
36}
37
38impl FulfillmentError<'_> {
39 pub fn is_true_error(&self) -> bool {
40 match self.code {
41 FulfillmentErrorCode::Select(_)
42 | FulfillmentErrorCode::Project(_)
43 | FulfillmentErrorCode::Subtype(_, _)
44 | FulfillmentErrorCode::ConstEquate(_, _) => true,
45 FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => {
46 false
47 }
48 }
49 }
50}
51
52#[derive(Clone)]
53pub enum FulfillmentErrorCode<'db> {
54 Cycle(PredicateObligations<'db>),
57 Select(SelectionError<'db>),
58 Project(MismatchedProjectionTypes<'db>),
59 Subtype(ExpectedFound<Ty<'db>>, TypeError<'db>), ConstEquate(ExpectedFound<Const<'db>>, TypeError<'db>),
61 Ambiguity {
62 overflow: Option<bool>,
66 },
67}
68
69impl<'db> fmt::Debug for FulfillmentErrorCode<'db> {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 match *self {
72 FulfillmentErrorCode::Select(ref e) => write!(f, "{e:?}"),
73 FulfillmentErrorCode::Project(ref e) => write!(f, "{e:?}"),
74 FulfillmentErrorCode::Subtype(ref a, ref b) => {
75 write!(f, "CodeSubtypeError({a:?}, {b:?})")
76 }
77 FulfillmentErrorCode::ConstEquate(ref a, ref b) => {
78 write!(f, "CodeConstEquateError({a:?}, {b:?})")
79 }
80 FulfillmentErrorCode::Ambiguity { overflow: None } => write!(f, "Ambiguity"),
81 FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => {
82 write!(f, "Overflow({suggest_increasing_limit})")
83 }
84 FulfillmentErrorCode::Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"),
85 }
86 }
87}
88
89#[derive(Clone)]
90pub struct MismatchedProjectionTypes<'db> {
91 pub err: TypeError<'db>,
92}
93
94impl<'db> fmt::Debug for MismatchedProjectionTypes<'db> {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 write!(f, "MismatchedProjectionTypes({:?})", self.err)
97 }
98}
99
100impl<'db> NextSolverError<'db> {
101 pub fn into_fulfillment_error(self, infcx: &InferCtxt<'db>) -> FulfillmentError<'db> {
102 match self {
103 NextSolverError::TrueError(obligation) => {
104 fulfillment_error_for_no_solution(infcx, obligation)
105 }
106 NextSolverError::Ambiguity(obligation) => {
107 fulfillment_error_for_stalled(infcx, obligation)
108 }
109 NextSolverError::Overflow(obligation) => {
110 fulfillment_error_for_overflow(infcx, obligation)
111 }
112 }
113 }
114}
115
116fn fulfillment_error_for_no_solution<'db>(
117 infcx: &InferCtxt<'db>,
118 root_obligation: PredicateObligation<'db>,
119) -> FulfillmentError<'db> {
120 let interner = infcx.interner;
121 let db = interner.db;
122 let (obligation, parent_trait_obligations) =
123 find_best_leaf_obligation(infcx, &root_obligation, false);
124
125 let code = match obligation.predicate.kind().skip_binder() {
126 PredicateKind::Clause(ClauseKind::Projection(_)) => {
127 FulfillmentErrorCode::Project(
128 MismatchedProjectionTypes { err: TypeError::Mismatch },
130 )
131 }
132 PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, expected_ty)) => {
133 let ct_ty = match ct.kind() {
134 ConstKind::Unevaluated(uv) => {
135 let ct_ty = match uv.def.0 {
136 GeneralConstId::ConstId(konst) => db.value_ty(konst.into()).unwrap(),
137 GeneralConstId::StaticId(statik) => db.value_ty(statik.into()).unwrap(),
138 GeneralConstId::AnonConstId(konst) => konst.loc(db).ty.get(),
139 };
140 ct_ty.instantiate(interner, uv.args).skip_norm_wip()
141 }
142 ConstKind::Param(param_ct) => param_ct.find_const_ty_from_env(obligation.param_env),
143 ConstKind::Value(cv) => cv.ty,
144 kind => panic!(
145 "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
146 ),
147 };
148 FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType {
149 ct,
150 ct_ty,
151 expected_ty,
152 })
153 }
154 PredicateKind::NormalizesTo(..) => {
155 FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
156 }
157 PredicateKind::AliasRelate(_, _, _) => {
158 FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
159 }
160 PredicateKind::Subtype(pred) => {
161 let (a, b) = infcx.enter_forall_and_leak_universe(
162 obligation.predicate.kind().rebind((pred.a, pred.b)),
163 );
164 let expected_found = ExpectedFound::new(a, b);
165 FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
166 }
167 PredicateKind::Coerce(pred) => {
168 let (a, b) = infcx.enter_forall_and_leak_universe(
169 obligation.predicate.kind().rebind((pred.a, pred.b)),
170 );
171 let expected_found = ExpectedFound::new(b, a);
172 FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
173 }
174 PredicateKind::Clause(_) | PredicateKind::DynCompatible(_) | PredicateKind::Ambiguous => {
175 FulfillmentErrorCode::Select(SelectionError::Unimplemented)
176 }
177 PredicateKind::ConstEquate(..) => {
178 panic!("unexpected goal: {obligation:?}")
179 }
180 };
181
182 FulfillmentError { obligation, code, parent_trait_obligations }
183}
184
185fn fulfillment_error_for_stalled<'db>(
186 infcx: &InferCtxt<'db>,
187 root_obligation: PredicateObligation<'db>,
188) -> FulfillmentError<'db> {
189 let (code, refine_obligation) = infcx.probe(|_| {
190 match <&SolverContext<'db>>::from(infcx).evaluate_root_goal(
191 root_obligation.as_goal(),
192 root_obligation.cause.span(),
193 None,
194 ) {
195 Ok(GoalEvaluation {
196 certainty: Certainty::Maybe(MaybeInfo { cause: MaybeCause::Ambiguity, .. }),
197 ..
198 }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true),
199 Ok(GoalEvaluation {
200 certainty:
201 Certainty::Maybe(MaybeInfo {
202 cause:
203 MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ },
204 ..
205 }),
206 ..
207 }) => (
208 FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
209 false,
216 ),
217 Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => {
218 panic!(
219 "did not expect successful goal when collecting ambiguity errors for `{:?}`",
220 infcx.resolve_vars_if_possible(root_obligation.predicate),
221 )
222 }
223 Err(_) => {
224 panic!(
225 "did not expect selection error when collecting ambiguity errors for `{:?}`",
226 infcx.resolve_vars_if_possible(root_obligation.predicate),
227 )
228 }
229 }
230 });
231
232 let (obligation, parent_trait_obligations) = if refine_obligation {
233 find_best_leaf_obligation(infcx, &root_obligation, true)
234 } else {
235 (root_obligation, Vec::new())
236 };
237
238 FulfillmentError { obligation, code, parent_trait_obligations }
239}
240
241fn fulfillment_error_for_overflow<'db>(
242 infcx: &InferCtxt<'db>,
243 root_obligation: PredicateObligation<'db>,
244) -> FulfillmentError<'db> {
245 let (obligation, parent_trait_obligations) =
246 find_best_leaf_obligation(infcx, &root_obligation, true);
247 FulfillmentError {
248 obligation,
249 code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
250 parent_trait_obligations,
251 }
252}
253
254#[instrument(level = "debug", skip(infcx), ret)]
255fn find_best_leaf_obligation<'db>(
256 infcx: &InferCtxt<'db>,
257 obligation: &PredicateObligation<'db>,
258 consider_ambiguities: bool,
259) -> (PredicateObligation<'db>, Vec<Predicate<'db>>) {
260 let obligation = infcx.resolve_vars_if_possible(obligation.clone());
261 let (obligation, parent_trait_obligations) = infcx
267 .fudge_inference_if_ok(|| {
268 let mut visitor = BestObligation {
269 obligation: obligation.clone(),
270 consider_ambiguities,
271 parent_trait_obligations: Vec::new(),
272 };
273 infcx
274 .visit_proof_tree(obligation.as_goal(), &mut visitor)
275 .break_value()
276 .ok_or(())
277 .map(|(obligation, parent_trait_obligations)| {
280 (obligation.cause, obligation, parent_trait_obligations)
281 })
282 })
283 .map(|(cause, obligation, parent_trait_obligations)| {
284 (PredicateObligation { cause, ..obligation }, parent_trait_obligations)
285 })
286 .unwrap_or((obligation, Vec::new()));
287 let parent_trait_obligations =
288 deeply_normalize_for_diagnostics(infcx, obligation.param_env, parent_trait_obligations);
289 let obligation = deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation);
290 (obligation, parent_trait_obligations)
291}
292
293struct BestObligation<'db> {
294 obligation: PredicateObligation<'db>,
295 consider_ambiguities: bool,
296 parent_trait_obligations: Vec<Predicate<'db>>,
297}
298
299impl<'db> BestObligation<'db> {
300 fn with_derived_obligation(
301 &mut self,
302 derived_obligation: PredicateObligation<'db>,
303 and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'db>>::Result,
304 ) -> <Self as ProofTreeVisitor<'db>>::Result {
305 let parent_predicate = self.obligation.predicate;
306 let should_push = parent_predicate.as_trait_clause().is_some()
307 && self.parent_trait_obligations.last() != Some(&parent_predicate);
308 if should_push {
309 self.parent_trait_obligations.push(parent_predicate);
310 }
311 let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
312 let result = and_then(self);
313 self.obligation = old_obligation;
314 if should_push {
315 self.parent_trait_obligations.pop();
316 }
317 result
318 }
319
320 fn break_with_current_obligation(&mut self) -> <Self as ProofTreeVisitor<'db>>::Result {
321 ControlFlow::Break((
322 self.obligation.clone(),
323 std::mem::take(&mut self.parent_trait_obligations),
324 ))
325 }
326
327 fn non_trivial_candidates<'a>(
332 &self,
333 goal: &'a inspect::InspectGoal<'a, 'db>,
334 ) -> Vec<inspect::InspectCandidate<'a, 'db>> {
335 let mut candidates = goal.candidates();
336 match self.consider_ambiguities {
337 true => {
338 candidates.retain(|candidate| candidate.result().is_ok());
342 }
343 false => {
344 candidates.retain(|c| !matches!(c.kind(), inspect::ProbeKind::RigidAlias { .. }));
347 if candidates.len() > 1 {
351 candidates.retain(|candidate| {
352 goal.infcx().probe(|_| {
353 candidate.instantiate_nested_goals(self.span()).iter().any(
354 |nested_goal| {
355 matches!(
356 nested_goal.source(),
357 GoalSource::ImplWhereBound
358 | GoalSource::AliasBoundConstCondition
359 | GoalSource::AliasWellFormed
360 ) && nested_goal.result().is_err()
361 },
362 )
363 })
364 });
365 }
366 }
367 }
368
369 candidates
370 }
371
372 fn visit_well_formed_goal(
376 &mut self,
377 candidate: &inspect::InspectCandidate<'_, 'db>,
378 term: Term<'db>,
379 ) -> <Self as ProofTreeVisitor<'db>>::Result {
380 let _ = (candidate, term);
381 self.break_with_current_obligation()
406 }
407
408 fn detect_error_in_self_ty_normalization(
412 &mut self,
413 goal: &inspect::InspectGoal<'_, 'db>,
414 self_ty: Ty<'db>,
415 ) -> <Self as ProofTreeVisitor<'db>>::Result {
416 assert!(!self.consider_ambiguities);
417 let interner = goal.infcx().interner;
418 if let TyKind::Alias(..) = self_ty.kind() {
419 let infer_term = goal.infcx().next_ty_var(self.obligation.cause.span());
420 let pred = PredicateKind::AliasRelate(
421 self_ty.into(),
422 infer_term.into(),
423 AliasRelationDirection::Equate,
424 );
425 let obligation =
426 Obligation::new(interner, self.obligation.cause, goal.goal().param_env, pred);
427 self.with_derived_obligation(obligation, |this| {
428 goal.infcx().visit_proof_tree_at_depth(
429 goal.goal().with(interner, pred),
430 goal.depth() + 1,
431 this,
432 )
433 })
434 } else {
435 ControlFlow::Continue(())
436 }
437 }
438
439 fn detect_trait_error_in_higher_ranked_projection(
447 &mut self,
448 goal: &inspect::InspectGoal<'_, 'db>,
449 ) -> <Self as ProofTreeVisitor<'db>>::Result {
450 let interner = goal.infcx().interner;
451 if let Some(projection_clause) = goal.goal().predicate.as_projection_clause()
452 && !projection_clause.bound_vars().is_empty()
453 {
454 let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(interner));
455 let obligation = Obligation::new(
456 interner,
457 self.obligation.cause,
458 goal.goal().param_env,
459 deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred),
460 );
461 self.with_derived_obligation(obligation, |this| {
462 goal.infcx().visit_proof_tree_at_depth(
463 goal.goal().with(interner, pred),
464 goal.depth() + 1,
465 this,
466 )
467 })
468 } else {
469 ControlFlow::Continue(())
470 }
471 }
472
473 fn detect_non_well_formed_assoc_item(
480 &mut self,
481 goal: &inspect::InspectGoal<'_, 'db>,
482 alias: AliasTerm<'db>,
483 ) -> <Self as ProofTreeVisitor<'db>>::Result {
484 let interner = goal.infcx().interner;
485 let obligation = Obligation::new(
486 interner,
487 self.obligation.cause,
488 goal.goal().param_env,
489 alias.trait_ref(interner),
490 );
491 self.with_derived_obligation(obligation, |this| {
492 goal.infcx().visit_proof_tree_at_depth(
493 goal.goal().with(interner, alias.trait_ref(interner)),
494 goal.depth() + 1,
495 this,
496 )
497 })
498 }
499
500 fn detect_error_from_empty_candidates(
503 &mut self,
504 goal: &inspect::InspectGoal<'_, 'db>,
505 ) -> <Self as ProofTreeVisitor<'db>>::Result {
506 let interner = goal.infcx().interner;
507 let pred_kind = goal.goal().predicate.kind();
508
509 match pred_kind.no_bound_vars() {
510 Some(PredicateKind::Clause(ClauseKind::Trait(pred))) => {
511 self.detect_error_in_self_ty_normalization(goal, pred.self_ty())?;
512 }
513 Some(PredicateKind::NormalizesTo(pred))
514 if let AliasTermKind::ProjectionTy { .. }
515 | AliasTermKind::ProjectionConst { .. } = pred.alias.kind(interner) =>
516 {
517 self.detect_error_in_self_ty_normalization(goal, pred.alias.self_ty())?;
518 self.detect_non_well_formed_assoc_item(goal, pred.alias)?;
519 }
520 Some(_) | None => {}
521 }
522
523 self.break_with_current_obligation()
524 }
525}
526
527impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> {
528 type Result = ControlFlow<(PredicateObligation<'db>, Vec<Predicate<'db>>)>;
529
530 fn span(&self) -> Span {
531 self.obligation.cause.span()
532 }
533
534 #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
535 fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'db>) -> Self::Result {
536 let interner = goal.infcx().interner;
537 match (self.consider_ambiguities, goal.result()) {
539 (true, Ok(Certainty::Maybe(MaybeInfo { cause: MaybeCause::Ambiguity, .. })))
540 | (false, Err(_)) => {}
541 _ => return ControlFlow::Continue(()),
542 }
543
544 let pred = goal.goal().predicate;
545
546 let candidates = self.non_trivial_candidates(goal);
547 let candidate = match candidates.as_slice() {
548 [candidate] => candidate,
549 [] => return self.detect_error_from_empty_candidates(goal),
550 _ => return self.break_with_current_obligation(),
551 };
552
553 if let inspect::ProbeKind::TraitCandidate {
555 source: CandidateSource::Impl(impl_def_id),
556 result: _,
557 } = candidate.kind()
558 && let AnyImplId::ImplId(impl_def_id) = impl_def_id
559 && AttrFlags::query(interner.db, impl_def_id.into())
560 .contains(AttrFlags::DIAGNOSTIC_DO_NOT_RECOMMEND)
561 {
562 trace!("#[diagnostic::do_not_recommend] -> exit");
563 return self.break_with_current_obligation();
564 }
565
566 let child_mode = match pred.kind().skip_binder() {
569 PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => {
570 ChildMode::Trait(pred.kind().rebind(trait_pred))
571 }
572 PredicateKind::Clause(ClauseKind::HostEffect(host_pred)) => {
573 ChildMode::Host(pred.kind().rebind(host_pred))
574 }
575 PredicateKind::NormalizesTo(normalizes_to)
576 if matches!(
577 normalizes_to.alias.kind(interner),
578 AliasTermKind::ProjectionTy { .. } | AliasTermKind::ProjectionConst { .. }
579 ) =>
580 {
581 ChildMode::Trait(pred.kind().rebind(TraitPredicate {
582 trait_ref: normalizes_to.alias.trait_ref(interner),
583 polarity: PredicatePolarity::Positive,
584 }))
585 }
586 PredicateKind::Clause(ClauseKind::WellFormed(term)) => {
587 return self.visit_well_formed_goal(candidate, term);
588 }
589 _ => ChildMode::PassThrough,
590 };
591
592 let nested_goals = candidate.instantiate_nested_goals(self.span());
593
594 for nested_goal in &nested_goals {
602 if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
603 && Some(poly_trait_pred.def_id().0) == interner.lang_items().FnPtrTrait
604 && let Err(NoSolution) = nested_goal.result()
605 {
606 return self.break_with_current_obligation();
607 }
608 }
609
610 let mut impl_where_bound_count = 0;
611 for nested_goal in nested_goals {
612 trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
613
614 let nested_pred = nested_goal.goal().predicate;
615
616 let make_obligation = |cause| Obligation {
617 cause,
618 param_env: nested_goal.goal().param_env,
619 predicate: nested_pred,
620 recursion_depth: self.obligation.recursion_depth + 1,
621 };
622
623 let obligation;
624 match (child_mode, nested_goal.source()) {
625 (
626 ChildMode::Trait(_) | ChildMode::Host(_),
627 GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_),
628 ) => {
629 continue;
630 }
631 (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
632 obligation = make_obligation(derive_cause(
633 interner,
634 candidate.kind(),
635 self.obligation.cause,
636 impl_where_bound_count,
637 parent_trait_pred,
638 ));
639 impl_where_bound_count += 1;
640 }
641 (
642 ChildMode::Host(parent_host_pred),
643 GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
644 ) => {
645 obligation = make_obligation(derive_host_cause(
646 interner,
647 candidate.kind(),
648 self.obligation.cause,
649 impl_where_bound_count,
650 parent_host_pred,
651 ));
652 impl_where_bound_count += 1;
653 }
654 (ChildMode::PassThrough, _)
655 | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
656 obligation = make_obligation(self.obligation.cause);
657 }
658 }
659
660 self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
661 }
662
663 if let Some(PredicateKind::AliasRelate(lhs, rhs, _)) = pred.kind().no_bound_vars() {
666 goal.infcx().visit_proof_tree_at_depth(
667 goal.goal().with(interner, ClauseKind::WellFormed(lhs)),
668 goal.depth() + 1,
669 self,
670 )?;
671 goal.infcx().visit_proof_tree_at_depth(
672 goal.goal().with(interner, ClauseKind::WellFormed(rhs)),
673 goal.depth() + 1,
674 self,
675 )?;
676 }
677
678 self.detect_trait_error_in_higher_ranked_projection(goal)?;
679
680 self.break_with_current_obligation()
681 }
682}
683
684#[derive(Debug, Copy, Clone)]
685enum ChildMode<'db> {
686 Trait(PolyTraitPredicate<'db>),
690 Host(Binder<'db, HostEffectPredicate<'db>>),
694 PassThrough,
698}
699
700fn derive_cause<'db>(
701 _interner: DbInterner<'db>,
702 _candidate_kind: inspect::ProbeKind<DbInterner<'db>>,
703 cause: ObligationCause,
704 _idx: usize,
705 _parent_trait_pred: PolyTraitPredicate<'db>,
706) -> ObligationCause {
707 cause
708}
709
710fn derive_host_cause<'db>(
711 _interner: DbInterner<'db>,
712 _candidate_kind: inspect::ProbeKind<DbInterner<'db>>,
713 cause: ObligationCause,
714 _idx: usize,
715 _parent_host_pred: Binder<'db, HostEffectPredicate<'db>>,
716) -> ObligationCause {
717 cause
718}