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