Skip to main content

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, ParamEnvSource};
6use rustc_infer::traits::{
7    BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, Obligation, ObligationCause,
8    PolyTraitObligation, Selection, SelectionError, SelectionResult,
9};
10use rustc_macros::extension;
11use rustc_span::{Span, bug, span_bug};
12use thin_vec::thin_vec;
13
14use crate::solve::inspect::{self, InferCtxtProofTreeExt};
15
16pub trait InferCtxtSelectExt<'tcx> {
    #[doc =
    " Do not use this directly. This is called from [`crate::traits::SelectionContext::poly_select`]."]
    fn select_in_new_trait_solver(&self,
    obligation: &PolyTraitObligation<'tcx>)
    -> SelectionResult<'tcx, Selection<'tcx>>;
}
impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
    #[doc =
    " Do not use this directly. This is called from [`crate::traits::SelectionContext::poly_select`]."]
    fn select_in_new_trait_solver(&self,
        obligation: &PolyTraitObligation<'tcx>)
        -> SelectionResult<'tcx, Selection<'tcx>> {
        if !self.next_trait_solver() {
            ::core::panicking::panic("assertion failed: self.next_trait_solver()")
        };
        self.visit_proof_tree(Goal::new(self.tcx, obligation.param_env,
                        obligation.predicate),
                    &mut Select {
                            span: obligation.cause.span,
                        }).break_value().unwrap()
    }
}#[extension(pub trait InferCtxtSelectExt<'tcx>)]
17impl<'tcx> InferCtxt<'tcx> {
18    /// Do not use this directly. This is called from [`crate::traits::SelectionContext::poly_select`].
19    fn select_in_new_trait_solver(
20        &self,
21        obligation: &PolyTraitObligation<'tcx>,
22    ) -> SelectionResult<'tcx, Selection<'tcx>> {
23        assert!(self.next_trait_solver());
24
25        self.visit_proof_tree(
26            Goal::new(self.tcx, obligation.param_env, obligation.predicate),
27            &mut Select { span: obligation.cause.span },
28        )
29        .break_value()
30        .unwrap()
31    }
32}
33
34struct Select {
35    span: Span,
36}
37
38impl<'tcx> inspect::ProofTreeVisitor<'tcx> for Select {
39    type Result = ControlFlow<SelectionResult<'tcx, Selection<'tcx>>>;
40
41    fn span(&self) -> Span {
42        self.span
43    }
44
45    fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
46        let mut candidates = goal.candidates();
47        candidates.retain(|cand| cand.result().is_ok());
48
49        // No candidates -- not implemented.
50        if candidates.is_empty() {
51            return ControlFlow::Break(Err(SelectionError::Unimplemented));
52        }
53
54        // One candidate, no need to winnow.
55        if candidates.len() == 1 {
56            return ControlFlow::Break(Ok(to_selection(
57                self.span,
58                candidates.into_iter().next().unwrap(),
59            )));
60        }
61
62        // Don't winnow until `Certainty::Yes` -- we don't need to winnow until
63        // codegen, and only on the good path.
64        if #[allow(non_exhaustive_omitted_patterns)] match goal.result().unwrap() {
    Certainty::Maybe(_) => true,
    _ => false,
}matches!(goal.result().unwrap(), Certainty::Maybe(_)) {
65            return ControlFlow::Break(Ok(None));
66        }
67
68        // We need to winnow. See comments on `candidate_should_be_dropped_in_favor_of`.
69        let mut i = 0;
70        while i < candidates.len() {
71            let should_drop_i = (0..candidates.len())
72                .filter(|&j| i != j)
73                .any(|j| candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j]));
74            if should_drop_i {
75                candidates.swap_remove(i);
76            } else {
77                i += 1;
78                if i > 1 {
79                    return ControlFlow::Break(Ok(None));
80                }
81            }
82        }
83
84        ControlFlow::Break(Ok(to_selection(self.span, candidates.into_iter().next().unwrap())))
85    }
86}
87
88/// This is a lot more limited than the old solver's equivalent method. This may lead to more `Ok(None)`
89/// results when selecting traits in polymorphic contexts, but we should never rely on the lack of ambiguity,
90/// and should always just gracefully fail here. We shouldn't rely on this incompleteness.
91fn candidate_should_be_dropped_in_favor_of<'tcx>(
92    victim: &inspect::InspectCandidate<'_, 'tcx>,
93    other: &inspect::InspectCandidate<'_, 'tcx>,
94) -> bool {
95    // Don't winnow until `Certainty::Yes` -- we don't need to winnow until constant evaluation or
96    // codegen.
97    if #[allow(non_exhaustive_omitted_patterns)] match other.result().unwrap() {
    Certainty::Maybe(_) => true,
    _ => false,
}matches!(other.result().unwrap(), Certainty::Maybe(_)) {
98        return false;
99    }
100
101    let inspect::ProbeKind::TraitCandidate { source: victim_source, result: _ } = victim.kind()
102    else {
103        return false;
104    };
105    let inspect::ProbeKind::TraitCandidate { source: other_source, result: _ } = other.kind()
106    else {
107        return false;
108    };
109
110    match (victim_source, other_source) {
111        (_, CandidateSource::CoherenceUnknowable) | (CandidateSource::CoherenceUnknowable, _) => {
112            ::rustc_span::macros::bug_impl(None,
    format_args!("should not have assembled a CoherenceUnknowable candidate"),
    Location::caller())bug!("should not have assembled a CoherenceUnknowable candidate")
113        }
114
115        // In the old trait solver, we arbitrarily choose lower vtable candidates
116        // over higher ones.
117        (
118            CandidateSource::BuiltinImpl(BuiltinImplSource::Object(a)),
119            CandidateSource::BuiltinImpl(BuiltinImplSource::Object(b)),
120        ) => a >= b,
121        (
122            CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(a)),
123            CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(b)),
124        ) => a >= b,
125        // Prefer dyn candidates over non-dyn candidates. This is necessary to
126        // handle the unsoundness between `impl<T: ?Sized> Any for T` and `dyn Any: Any`.
127        (
128            CandidateSource::Impl(_)
129            | CandidateSource::ParamEnv(_)
130            | CandidateSource::AliasBound(_),
131            CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }),
132        ) => true,
133
134        // Prefer specializing candidates over specialized candidates.
135        (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => {
136            victim.goal().infcx().tcx.specializes((other_def_id, victim_def_id))
137        }
138
139        // Prefer impl candidates over global where clause candidates. Unless `generic_const_args`
140        // is enabled, we currently don't use an empty environment when resolving and evaluating
141        // constants to lower them to patterns. If we don't drop where clause candidates here, we
142        // can fail to select impl candidates (#162331).
143        (
144            CandidateSource::ParamEnv(ParamEnvSource::Global),
145            CandidateSource::Impl(_) | CandidateSource::BuiltinImpl(_),
146        ) => true,
147
148        _ => false,
149    }
150}
151
152fn to_selection<'tcx>(
153    span: Span,
154    cand: inspect::InspectCandidate<'_, 'tcx>,
155) -> Option<Selection<'tcx>> {
156    if let Certainty::Maybe(_) = cand.shallow_certainty() {
157        return None;
158    }
159
160    let nested = match cand.result().expect("expected positive result") {
161        Certainty::Yes => ::thin_vec::ThinVec::new()thin_vec![],
162        Certainty::Maybe(_) => cand
163            .instantiate_nested_goals(span)
164            .into_iter()
165            .map(|nested| {
166                Obligation::new(
167                    nested.infcx().tcx,
168                    ObligationCause::dummy_with_span(span),
169                    nested.goal().param_env,
170                    nested.goal().predicate,
171                )
172            })
173            .collect(),
174    };
175
176    Some(match cand.kind() {
177        ProbeKind::TraitCandidate { source, result: _ } => match source {
178            CandidateSource::Impl(impl_def_id) => {
179                // FIXME: Remove this in favor of storing this in the tree
180                // For impl candidates, we do the rematch manually to compute the args.
181                ImplSource::UserDefined(ImplSourceUserDefinedData {
182                    impl_def_id,
183                    args: cand.instantiate_impl_args(span),
184                    nested,
185                })
186            }
187            CandidateSource::BuiltinImpl(builtin) => ImplSource::Builtin(builtin, nested),
188            CandidateSource::ParamEnv(_) | CandidateSource::AliasBound(_) => {
189                ImplSource::Param(nested)
190            }
191            CandidateSource::CoherenceUnknowable => {
192                ::rustc_span::macros::bug_impl(Some(span),
    format_args!("didn\'t expect to select an unknowable candidate"),
    Location::caller())span_bug!(span, "didn't expect to select an unknowable candidate")
193            }
194        },
195        ProbeKind::NormalizedSelfTyAssembly
196        | ProbeKind::UnsizeAssembly
197        | ProbeKind::ProjectionCompatibility
198        | ProbeKind::OpaqueTypeStorageLookup { result: _ }
199        | ProbeKind::Root { result: _ }
200        | ProbeKind::ShadowedEnvProbing
201        | ProbeKind::RigidAlias { result: _ } => {
202            ::rustc_span::macros::bug_impl(Some(span),
    format_args!("didn\'t expect to assemble trait candidate from {0:#?}",
        cand.kind()), Location::caller())span_bug!(span, "didn't expect to assemble trait candidate from {:#?}", cand.kind())
203        }
204    })
205}