]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/solve/trait_goals.rs
dedup assembly
[rust.git] / compiler / rustc_trait_selection / src / solve / trait_goals.rs
1 //! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
2
3 use std::iter;
4
5 use super::assembly::{self, AssemblyCtxt};
6 use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult};
7 use rustc_hir::def_id::DefId;
8 use rustc_infer::infer::InferOk;
9 use rustc_infer::traits::query::NoSolution;
10 use rustc_infer::traits::ObligationCause;
11 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
12 use rustc_middle::ty::TraitPredicate;
13 use rustc_middle::ty::{self, Ty, TyCtxt};
14 use rustc_span::DUMMY_SP;
15
16 #[allow(dead_code)] // FIXME: implement and use all variants.
17 #[derive(Debug, Clone, Copy)]
18 pub(super) enum CandidateSource {
19     /// Some user-defined impl with the given `DefId`.
20     Impl(DefId),
21     /// The n-th caller bound in the `param_env` of our goal.
22     ///
23     /// This is pretty much always a bound from the `where`-clauses of the
24     /// currently checked item.
25     ParamEnv(usize),
26     /// A bound on the `self_ty` in case it is a projection or an opaque type.
27     ///
28     /// # Examples
29     ///
30     /// ```ignore (for syntax highlighting)
31     /// trait Trait {
32     ///     type Assoc: OtherTrait;
33     /// }
34     /// ```
35     ///
36     /// We know that `<Whatever as Trait>::Assoc: OtherTrait` holds by looking at
37     /// the bounds on `Trait::Assoc`.
38     AliasBound(usize),
39     /// A builtin implementation for some specific traits, used in cases
40     /// where we cannot rely an ordinary library implementations.
41     ///
42     /// The most notable examples are `Sized`, `Copy` and `Clone`. This is also
43     /// used for the `DiscriminantKind` and `Pointee` trait, both of which have
44     /// an associated type.
45     Builtin,
46     /// An automatic impl for an auto trait, e.g. `Send`. These impls recursively look
47     /// at the constituent types of the `self_ty` to check whether the auto trait
48     /// is implemented for those.
49     AutoImpl,
50 }
51
52 type Candidate<'tcx> = assembly::Candidate<'tcx, TraitPredicate<'tcx>>;
53
54 impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
55     type CandidateSource = CandidateSource;
56
57     fn self_ty(self) -> Ty<'tcx> {
58         self.self_ty()
59     }
60
61     fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
62         self.with_self_ty(tcx, self_ty)
63     }
64
65     fn trait_def_id(self, _: TyCtxt<'tcx>) -> DefId {
66         self.def_id()
67     }
68
69     fn consider_impl_candidate(
70         acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
71         goal: Goal<'tcx, TraitPredicate<'tcx>>,
72         impl_def_id: DefId,
73     ) {
74         let impl_trait_ref = acx.cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap();
75         let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder };
76         if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs)
77             .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp))
78         {
79             return;
80         }
81
82         acx.infcx.probe(|_| {
83             let impl_substs = acx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
84             let impl_trait_ref = impl_trait_ref.subst(acx.cx.tcx, impl_substs);
85
86             let Ok(InferOk { obligations, .. }) = acx
87                 .infcx
88                 .at(&ObligationCause::dummy(), goal.param_env)
89                 .define_opaque_types(false)
90                 .eq(goal.predicate.trait_ref, impl_trait_ref)
91                 .map_err(|e| debug!("failed to equate trait refs: {e:?}"))
92             else {
93                 return
94             };
95
96             let nested_goals = obligations.into_iter().map(|o| o.into()).collect();
97
98             let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
99             acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
100         })
101     }
102 }
103
104 impl<'tcx> EvalCtxt<'tcx> {
105     pub(super) fn compute_trait_goal(
106         &mut self,
107         goal: CanonicalGoal<'tcx, TraitPredicate<'tcx>>,
108     ) -> QueryResult<'tcx> {
109         let candidates = AssemblyCtxt::assemble_and_evaluate_candidates(self, goal);
110         self.merge_trait_candidates_discard_reservation_impls(candidates)
111     }
112
113     #[instrument(level = "debug", skip(self), ret)]
114     pub(super) fn merge_trait_candidates_discard_reservation_impls(
115         &mut self,
116         mut candidates: Vec<Candidate<'tcx>>,
117     ) -> QueryResult<'tcx> {
118         match candidates.len() {
119             0 => return Err(NoSolution),
120             1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result),
121             _ => {}
122         }
123
124         if candidates.len() > 1 {
125             let mut i = 0;
126             'outer: while i < candidates.len() {
127                 for j in (0..candidates.len()).filter(|&j| i != j) {
128                     if self.trait_candidate_should_be_dropped_in_favor_of(
129                         &candidates[i],
130                         &candidates[j],
131                     ) {
132                         debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
133                         candidates.swap_remove(i);
134                         continue 'outer;
135                     }
136                 }
137
138                 debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
139                 // If there are *STILL* multiple candidates, give up
140                 // and report ambiguity.
141                 i += 1;
142                 if i > 1 {
143                     debug!("multiple matches, ambig");
144                     // FIXME: return overflow if all candidates overflow, otherwise return ambiguity.
145                     unimplemented!();
146                 }
147             }
148         }
149
150         Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result)
151     }
152
153     fn trait_candidate_should_be_dropped_in_favor_of(
154         &self,
155         candidate: &Candidate<'tcx>,
156         other: &Candidate<'tcx>,
157     ) -> bool {
158         // FIXME: implement this
159         match (candidate.source, other.source) {
160             (CandidateSource::Impl(_), _)
161             | (CandidateSource::ParamEnv(_), _)
162             | (CandidateSource::AliasBound(_), _)
163             | (CandidateSource::Builtin, _)
164             | (CandidateSource::AutoImpl, _) => unimplemented!(),
165         }
166     }
167
168     fn discard_reservation_impl(&self, candidate: Candidate<'tcx>) -> Candidate<'tcx> {
169         if let CandidateSource::Impl(def_id) = candidate.source {
170             if let ty::ImplPolarity::Reservation = self.tcx.impl_polarity(def_id) {
171                 debug!("Selected reservation impl");
172                 // FIXME: reduce candidate to ambiguous
173                 // FIXME: replace `var_values` with identity, yeet external constraints.
174                 unimplemented!()
175             }
176         }
177
178         candidate
179     }
180 }