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        // No candidates -- not implemented.
49        if candidates.is_empty() {
50            return ControlFlow::Break(Err(SelectionError::Unimplemented));
51        }
52
53        // One candidate, no need to winnow.
54        if candidates.len() == 1 {
55            return ControlFlow::Break(Ok(to_selection(
56                self.span,
57                candidates.into_iter().next().unwrap(),
58            )));
59        }
60
61        // Don't winnow until `Certainty::Yes` -- we don't need to winnow until
62        // codegen, and only on the good path.
63        if matches!(goal.result().unwrap(), Certainty::Maybe(..)) {
64            return ControlFlow::Break(Ok(None));
65        }
66
67        // We need to winnow. See comments on `candidate_should_be_dropped_in_favor_of`.
68        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
87/// This is a lot more limited than the old solver's equivalent method. This may lead to more `Ok(None)`
88/// results when selecting traits in polymorphic contexts, but we should never rely on the lack of ambiguity,
89/// and should always just gracefully fail here. We shouldn't rely on this incompleteness.
90fn candidate_should_be_dropped_in_favor_of<'tcx>(
91    victim: &inspect::InspectCandidate<'_, 'tcx>,
92    other: &inspect::InspectCandidate<'_, 'tcx>,
93) -> bool {
94    // Don't winnow until `Certainty::Yes` -- we don't need to winnow until
95    // codegen, and only on the good path.
96    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        // In the old trait solver, we arbitrarily choose lower vtable candidates
115        // over higher ones.
116        (
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        // Prefer dyn candidates over non-dyn candidates. This is necessary to
125        // handle the unsoundness between `impl<T: ?Sized> Any for T` and `dyn Any: Any`.
126        (
127            CandidateSource::Impl(_) | CandidateSource::ParamEnv(_) | CandidateSource::AliasBound,
128            CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
129        ) => true,
130
131        // Prefer specializing candidates over specialized candidates.
132        (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                // FIXME: Remove this in favor of storing this in the tree
165                // For impl candidates, we do the rematch manually to compute the args.
166                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}