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 bug!(
124 "did not expect successful goal when collecting ambiguity errors for `{:?}`",
125 infcx.resolve_vars_if_possible(root_obligation.predicate),
126 )
127 }
128 Err(_) => {
129 bug!(
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 })
181 .unwrap_or(obligation);
182 deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation)
183}
184
185struct BestObligation<'tcx> {
186 obligation: PredicateObligation<'tcx>,
187 consider_ambiguities: bool,
188}
189
190impl<'tcx> BestObligation<'tcx> {
191 fn with_derived_obligation(
192 &mut self,
193 derived_obligation: PredicateObligation<'tcx>,
194 and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'tcx>>::Result,
195 ) -> <Self as ProofTreeVisitor<'tcx>>::Result {
196 let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
197 let res = and_then(self);
198 self.obligation = old_obligation;
199 res
200 }
201
202 fn non_trivial_candidates<'a>(
207 &self,
208 goal: &'a inspect::InspectGoal<'a, 'tcx>,
209 ) -> Vec<inspect::InspectCandidate<'a, 'tcx>> {
210 let mut candidates = goal.candidates();
211 match self.consider_ambiguities {
212 true => {
213 candidates.retain(|candidate| candidate.result().is_ok());
217 }
218 false => {
219 candidates.retain(|c| !matches!(c.kind(), inspect::ProbeKind::RigidAlias { .. }));
222 if candidates.len() > 1 {
226 candidates.retain(|candidate| {
227 goal.infcx().probe(|_| {
228 candidate.instantiate_nested_goals(self.span()).iter().any(
229 |nested_goal| {
230 matches!(
231 nested_goal.source(),
232 GoalSource::ImplWhereBound
233 | GoalSource::AliasBoundConstCondition
234 | GoalSource::InstantiateHigherRanked
235 | GoalSource::AliasWellFormed
236 ) && nested_goal.result().is_err()
237 },
238 )
239 })
240 });
241 }
242 }
243 }
244
245 candidates
246 }
247
248 fn visit_well_formed_goal(
252 &mut self,
253 candidate: &inspect::InspectCandidate<'_, 'tcx>,
254 term: ty::Term<'tcx>,
255 ) -> ControlFlow<PredicateObligation<'tcx>> {
256 let infcx = candidate.goal().infcx();
257 let param_env = candidate.goal().goal().param_env;
258 let body_id = self.obligation.cause.body_id;
259
260 for obligation in wf::unnormalized_obligations(infcx, param_env, term, self.span(), body_id)
261 .into_iter()
262 .flatten()
263 {
264 let nested_goal = candidate.instantiate_proof_tree_for_nested_goal(
265 GoalSource::Misc,
266 obligation.as_goal(),
267 self.span(),
268 );
269 match (self.consider_ambiguities, nested_goal.result()) {
271 (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
272 _ => continue,
273 }
274
275 self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
276 }
277
278 ControlFlow::Break(self.obligation.clone())
279 }
280
281 fn detect_error_in_self_ty_normalization(
285 &mut self,
286 goal: &inspect::InspectGoal<'_, 'tcx>,
287 self_ty: Ty<'tcx>,
288 ) -> ControlFlow<PredicateObligation<'tcx>> {
289 assert!(!self.consider_ambiguities);
290 let tcx = goal.infcx().tcx;
291 if let ty::Alias(..) = self_ty.kind() {
292 let infer_term = goal.infcx().next_ty_var(self.obligation.cause.span);
293 let pred = ty::PredicateKind::AliasRelate(
294 self_ty.into(),
295 infer_term.into(),
296 ty::AliasRelationDirection::Equate,
297 );
298 let obligation =
299 Obligation::new(tcx, self.obligation.cause.clone(), goal.goal().param_env, pred);
300 self.with_derived_obligation(obligation, |this| {
301 goal.infcx().visit_proof_tree_at_depth(
302 goal.goal().with(tcx, pred),
303 goal.depth() + 1,
304 this,
305 )
306 })
307 } else {
308 ControlFlow::Continue(())
309 }
310 }
311
312 fn detect_trait_error_in_higher_ranked_projection(
320 &mut self,
321 goal: &inspect::InspectGoal<'_, 'tcx>,
322 ) -> ControlFlow<PredicateObligation<'tcx>> {
323 let tcx = goal.infcx().tcx;
324 if let Some(projection_clause) = goal.goal().predicate.as_projection_clause()
325 && !projection_clause.bound_vars().is_empty()
326 {
327 let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(tcx));
328 let obligation = Obligation::new(
329 tcx,
330 self.obligation.cause.clone(),
331 goal.goal().param_env,
332 deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred),
333 );
334 self.with_derived_obligation(obligation, |this| {
335 goal.infcx().visit_proof_tree_at_depth(
336 goal.goal().with(tcx, pred),
337 goal.depth() + 1,
338 this,
339 )
340 })
341 } else {
342 ControlFlow::Continue(())
343 }
344 }
345
346 fn detect_non_well_formed_assoc_item(
353 &mut self,
354 goal: &inspect::InspectGoal<'_, 'tcx>,
355 alias: ty::AliasTerm<'tcx>,
356 ) -> ControlFlow<PredicateObligation<'tcx>> {
357 let tcx = goal.infcx().tcx;
358 let obligation = Obligation::new(
359 tcx,
360 self.obligation.cause.clone(),
361 goal.goal().param_env,
362 alias.trait_ref(tcx),
363 );
364 self.with_derived_obligation(obligation, |this| {
365 goal.infcx().visit_proof_tree_at_depth(
366 goal.goal().with(tcx, alias.trait_ref(tcx)),
367 goal.depth() + 1,
368 this,
369 )
370 })
371 }
372
373 fn detect_error_from_empty_candidates(
376 &mut self,
377 goal: &inspect::InspectGoal<'_, 'tcx>,
378 ) -> ControlFlow<PredicateObligation<'tcx>> {
379 let tcx = goal.infcx().tcx;
380 let pred_kind = goal.goal().predicate.kind();
381
382 match pred_kind.no_bound_vars() {
383 Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))) => {
384 self.detect_error_in_self_ty_normalization(goal, pred.self_ty())?;
385 }
386 Some(ty::PredicateKind::NormalizesTo(pred))
387 if let ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst =
388 pred.alias.kind(tcx) =>
389 {
390 self.detect_error_in_self_ty_normalization(goal, pred.alias.self_ty())?;
391 self.detect_non_well_formed_assoc_item(goal, pred.alias)?;
392 }
393 Some(_) | None => {}
394 }
395
396 ControlFlow::Break(self.obligation.clone())
397 }
398}
399
400impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
401 type Result = ControlFlow<PredicateObligation<'tcx>>;
402
403 fn span(&self) -> rustc_span::Span {
404 self.obligation.cause.span
405 }
406
407 #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
408 fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
409 let tcx = goal.infcx().tcx;
410 match (self.consider_ambiguities, goal.result()) {
412 (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
413 _ => return ControlFlow::Continue(()),
414 }
415
416 let pred = goal.goal().predicate;
417
418 let candidates = self.non_trivial_candidates(goal);
419 let candidate = match candidates.as_slice() {
420 [candidate] => candidate,
421 [] => return self.detect_error_from_empty_candidates(goal),
422 _ => return ControlFlow::Break(self.obligation.clone()),
423 };
424
425 if let inspect::ProbeKind::TraitCandidate {
427 source: CandidateSource::Impl(impl_def_id),
428 result: _,
429 } = candidate.kind()
430 && tcx.do_not_recommend_impl(impl_def_id)
431 {
432 trace!("#[do_not_recommend] -> exit");
433 return ControlFlow::Break(self.obligation.clone());
434 }
435
436 let child_mode = match pred.kind().skip_binder() {
439 ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {
440 ChildMode::Trait(pred.kind().rebind(trait_pred))
441 }
442 ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(host_pred)) => {
443 ChildMode::Host(pred.kind().rebind(host_pred))
444 }
445 ty::PredicateKind::NormalizesTo(normalizes_to)
446 if matches!(
447 normalizes_to.alias.kind(tcx),
448 ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst
449 ) =>
450 {
451 ChildMode::Trait(pred.kind().rebind(ty::TraitPredicate {
452 trait_ref: normalizes_to.alias.trait_ref(tcx),
453 polarity: ty::PredicatePolarity::Positive,
454 }))
455 }
456 ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => {
457 return self.visit_well_formed_goal(candidate, term);
458 }
459 _ => ChildMode::PassThrough,
460 };
461
462 let nested_goals = candidate.instantiate_nested_goals(self.span());
463
464 for nested_goal in &nested_goals {
472 if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
473 && tcx.is_lang_item(poly_trait_pred.def_id(), LangItem::FnPtrTrait)
474 && let Err(NoSolution) = nested_goal.result()
475 {
476 return ControlFlow::Break(self.obligation.clone());
477 }
478 }
479
480 let mut impl_where_bound_count = 0;
481 for nested_goal in nested_goals {
482 trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
483
484 let nested_pred = nested_goal.goal().predicate;
485
486 let make_obligation = |cause| Obligation {
487 cause,
488 param_env: nested_goal.goal().param_env,
489 predicate: nested_pred,
490 recursion_depth: self.obligation.recursion_depth + 1,
491 };
492
493 let obligation;
494 match (child_mode, nested_goal.source()) {
495 (
496 ChildMode::Trait(_) | ChildMode::Host(_),
497 GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_),
498 ) => {
499 continue;
500 }
501 (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
502 obligation = make_obligation(derive_cause(
503 tcx,
504 candidate.kind(),
505 self.obligation.cause.clone(),
506 impl_where_bound_count,
507 parent_trait_pred,
508 ));
509 impl_where_bound_count += 1;
510 }
511 (
512 ChildMode::Host(parent_host_pred),
513 GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
514 ) => {
515 obligation = make_obligation(derive_host_cause(
516 tcx,
517 candidate.kind(),
518 self.obligation.cause.clone(),
519 impl_where_bound_count,
520 parent_host_pred,
521 ));
522 impl_where_bound_count += 1;
523 }
524 (_, GoalSource::InstantiateHigherRanked) => {
526 obligation = self.obligation.clone();
527 }
528 (ChildMode::PassThrough, _)
529 | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
530 obligation = make_obligation(self.obligation.cause.clone());
531 }
532 }
533
534 self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
535 }
536
537 if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred.kind().no_bound_vars() {
540 goal.infcx().visit_proof_tree_at_depth(
541 goal.goal().with(tcx, ty::ClauseKind::WellFormed(lhs.into())),
542 goal.depth() + 1,
543 self,
544 )?;
545 goal.infcx().visit_proof_tree_at_depth(
546 goal.goal().with(tcx, ty::ClauseKind::WellFormed(rhs.into())),
547 goal.depth() + 1,
548 self,
549 )?;
550 }
551
552 self.detect_trait_error_in_higher_ranked_projection(goal)?;
553
554 ControlFlow::Break(self.obligation.clone())
555 }
556}
557
558#[derive(Debug, Copy, Clone)]
559enum ChildMode<'tcx> {
560 Trait(ty::PolyTraitPredicate<'tcx>),
564 Host(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>),
568 PassThrough,
572}
573
574fn derive_cause<'tcx>(
575 tcx: TyCtxt<'tcx>,
576 candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>,
577 mut cause: ObligationCause<'tcx>,
578 idx: usize,
579 parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
580) -> ObligationCause<'tcx> {
581 match candidate_kind {
582 inspect::ProbeKind::TraitCandidate {
583 source: CandidateSource::Impl(impl_def_id),
584 result: _,
585 } => {
586 if let Some((_, span)) =
587 tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx)
588 {
589 cause = cause.derived_cause(parent_trait_pred, |derived| {
590 ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause {
591 derived,
592 impl_or_alias_def_id: impl_def_id,
593 impl_def_predicate_index: Some(idx),
594 span,
595 }))
596 })
597 }
598 }
599 inspect::ProbeKind::TraitCandidate {
600 source: CandidateSource::BuiltinImpl(..),
601 result: _,
602 } => {
603 cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived);
604 }
605 _ => {}
606 };
607 cause
608}
609
610fn derive_host_cause<'tcx>(
611 tcx: TyCtxt<'tcx>,
612 candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>,
613 mut cause: ObligationCause<'tcx>,
614 idx: usize,
615 parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
616) -> ObligationCause<'tcx> {
617 match candidate_kind {
618 inspect::ProbeKind::TraitCandidate {
619 source: CandidateSource::Impl(impl_def_id),
620 result: _,
621 } => {
622 if let Some((_, span)) = tcx
623 .predicates_of(impl_def_id)
624 .instantiate_identity(tcx)
625 .into_iter()
626 .chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map(
627 |(trait_ref, span)| {
628 (
629 trait_ref.to_host_effect_clause(
630 tcx,
631 parent_host_pred.skip_binder().constness,
632 ),
633 span,
634 )
635 },
636 ))
637 .nth(idx)
638 {
639 cause =
640 cause.derived_host_cause(parent_host_pred, |derived| {
641 ObligationCauseCode::ImplDerivedHost(Box::new(
642 traits::ImplDerivedHostCause { derived, impl_def_id, span },
643 ))
644 })
645 }
646 }
647 inspect::ProbeKind::TraitCandidate {
648 source: CandidateSource::BuiltinImpl(..),
649 result: _,
650 } => {
651 cause =
652 cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost);
653 }
654 _ => {}
655 };
656 cause
657}