rustc_trait_selection/solve/
select.rs
1use std::ops::ControlFlow;
2
3use rustc_infer::infer::InferCtxt;
4use rustc_infer::traits::solve::inspect::ProbeKind;
5use rustc_infer::traits::solve::{CandidateSource, Certainty, Goal};
6use rustc_infer::traits::{
7 BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, Obligation, ObligationCause,
8 PolyTraitObligation, Selection, SelectionError, SelectionResult,
9};
10use rustc_macros::extension;
11use rustc_middle::{bug, span_bug};
12use rustc_span::Span;
13
14use crate::solve::inspect::{self, ProofTreeInferCtxtExt};
15
16#[extension(pub trait InferCtxtSelectExt<'tcx>)]
17impl<'tcx> InferCtxt<'tcx> {
18 fn select_in_new_trait_solver(
19 &self,
20 obligation: &PolyTraitObligation<'tcx>,
21 ) -> SelectionResult<'tcx, Selection<'tcx>> {
22 assert!(self.next_trait_solver());
23
24 self.visit_proof_tree(
25 Goal::new(self.tcx, obligation.param_env, obligation.predicate),
26 &mut Select { span: obligation.cause.span },
27 )
28 .break_value()
29 .unwrap()
30 }
31}
32
33struct Select {
34 span: Span,
35}
36
37impl<'tcx> inspect::ProofTreeVisitor<'tcx> for Select {
38 type Result = ControlFlow<SelectionResult<'tcx, Selection<'tcx>>>;
39
40 fn span(&self) -> Span {
41 self.span
42 }
43
44 fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
45 let mut candidates = goal.candidates();
46 candidates.retain(|cand| cand.result().is_ok());
47
48 if candidates.is_empty() {
50 return ControlFlow::Break(Err(SelectionError::Unimplemented));
51 }
52
53 if candidates.len() == 1 {
55 return ControlFlow::Break(Ok(to_selection(
56 self.span,
57 candidates.into_iter().next().unwrap(),
58 )));
59 }
60
61 if matches!(goal.result().unwrap(), Certainty::Maybe(..)) {
64 return ControlFlow::Break(Ok(None));
65 }
66
67 let mut i = 0;
69 while i < candidates.len() {
70 let should_drop_i = (0..candidates.len())
71 .filter(|&j| i != j)
72 .any(|j| candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j]));
73 if should_drop_i {
74 candidates.swap_remove(i);
75 } else {
76 i += 1;
77 if i > 1 {
78 return ControlFlow::Break(Ok(None));
79 }
80 }
81 }
82
83 ControlFlow::Break(Ok(to_selection(self.span, candidates.into_iter().next().unwrap())))
84 }
85}
86
87fn candidate_should_be_dropped_in_favor_of<'tcx>(
91 victim: &inspect::InspectCandidate<'_, 'tcx>,
92 other: &inspect::InspectCandidate<'_, 'tcx>,
93) -> bool {
94 if matches!(other.result().unwrap(), Certainty::Maybe(..)) {
97 return false;
98 }
99
100 let inspect::ProbeKind::TraitCandidate { source: victim_source, result: _ } = victim.kind()
101 else {
102 return false;
103 };
104 let inspect::ProbeKind::TraitCandidate { source: other_source, result: _ } = other.kind()
105 else {
106 return false;
107 };
108
109 match (victim_source, other_source) {
110 (_, CandidateSource::CoherenceUnknowable) | (CandidateSource::CoherenceUnknowable, _) => {
111 bug!("should not have assembled a CoherenceUnknowable candidate")
112 }
113
114 (
117 CandidateSource::BuiltinImpl(BuiltinImplSource::Object(a)),
118 CandidateSource::BuiltinImpl(BuiltinImplSource::Object(b)),
119 ) => a >= b,
120 (
121 CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(a)),
122 CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(b)),
123 ) => a >= b,
124 (
127 CandidateSource::Impl(_) | CandidateSource::ParamEnv(_) | CandidateSource::AliasBound,
128 CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
129 ) => true,
130
131 (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => {
133 victim.goal().infcx().tcx.specializes((other_def_id, victim_def_id))
134 }
135
136 _ => false,
137 }
138}
139
140fn to_selection<'tcx>(
141 span: Span,
142 cand: inspect::InspectCandidate<'_, 'tcx>,
143) -> Option<Selection<'tcx>> {
144 if let Certainty::Maybe(..) = cand.shallow_certainty() {
145 return None;
146 }
147
148 let (nested, impl_args) = cand.instantiate_nested_goals_and_opt_impl_args(span);
149 let nested = nested
150 .into_iter()
151 .map(|nested| {
152 Obligation::new(
153 nested.infcx().tcx,
154 ObligationCause::dummy_with_span(span),
155 nested.goal().param_env,
156 nested.goal().predicate,
157 )
158 })
159 .collect();
160
161 Some(match cand.kind() {
162 ProbeKind::TraitCandidate { source, result: _ } => match source {
163 CandidateSource::Impl(impl_def_id) => {
164 ImplSource::UserDefined(ImplSourceUserDefinedData {
167 impl_def_id,
168 args: impl_args.expect("expected recorded impl args for impl candidate"),
169 nested,
170 })
171 }
172 CandidateSource::BuiltinImpl(builtin) => ImplSource::Builtin(builtin, nested),
173 CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => ImplSource::Param(nested),
174 CandidateSource::CoherenceUnknowable => {
175 span_bug!(span, "didn't expect to select an unknowable candidate")
176 }
177 },
178 ProbeKind::NormalizedSelfTyAssembly
179 | ProbeKind::UnsizeAssembly
180 | ProbeKind::UpcastProjectionCompatibility
181 | ProbeKind::OpaqueTypeStorageLookup { result: _ }
182 | ProbeKind::Root { result: _ }
183 | ProbeKind::ShadowedEnvProbing
184 | ProbeKind::RigidAlias { result: _ } => {
185 span_bug!(span, "didn't expect to assemble trait candidate from {:#?}", cand.kind())
186 }
187 })
188}