1 //! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
5 use super::assembly::{self, Candidate, CandidateSource};
6 use super::infcx_ext::InferCtxtExt;
7 use super::{Certainty, EvalCtxt, Goal, QueryResult};
8 use rustc_hir::def_id::DefId;
9 use rustc_infer::infer::InferCtxt;
10 use rustc_infer::traits::query::NoSolution;
11 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
12 use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
13 use rustc_middle::ty::{TraitPredicate, TypeVisitable};
14 use rustc_span::DUMMY_SP;
16 pub mod structural_traits;
18 impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
19 fn self_ty(self) -> Ty<'tcx> {
23 fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
24 self.with_self_ty(tcx, self_ty)
27 fn trait_def_id(self, _: TyCtxt<'tcx>) -> DefId {
31 fn consider_impl_candidate(
32 ecx: &mut EvalCtxt<'_, 'tcx>,
33 goal: Goal<'tcx, TraitPredicate<'tcx>>,
35 ) -> QueryResult<'tcx> {
38 let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
39 let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder };
40 if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs)
41 .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp))
43 return Err(NoSolution);
47 let impl_substs = ecx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
48 let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
50 let mut nested_goals =
51 ecx.infcx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
52 let where_clause_bounds = tcx
53 .predicates_of(impl_def_id)
54 .instantiate(tcx, impl_substs)
57 .map(|pred| goal.with(tcx, pred));
58 nested_goals.extend(where_clause_bounds);
59 ecx.evaluate_all_and_make_canonical_response(nested_goals)
63 fn consider_assumption(
64 ecx: &mut EvalCtxt<'_, 'tcx>,
65 goal: Goal<'tcx, Self>,
66 assumption: ty::Predicate<'tcx>,
67 ) -> QueryResult<'tcx> {
68 if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred()
69 && poly_trait_pred.def_id() == goal.predicate.def_id()
71 // FIXME: Constness and polarity
73 let assumption_trait_pred =
74 ecx.infcx.instantiate_bound_vars_with_infer(poly_trait_pred);
75 let nested_goals = ecx.infcx.eq(
77 goal.predicate.trait_ref,
78 assumption_trait_pred.trait_ref,
80 ecx.evaluate_all_and_make_canonical_response(nested_goals)
87 fn consider_auto_trait_candidate(
88 ecx: &mut EvalCtxt<'_, 'tcx>,
89 goal: Goal<'tcx, Self>,
90 ) -> QueryResult<'tcx> {
91 ecx.probe_and_evaluate_goal_for_constituent_tys(
93 structural_traits::instantiate_constituent_tys_for_auto_trait,
97 fn consider_trait_alias_candidate(
98 ecx: &mut EvalCtxt<'_, 'tcx>,
99 goal: Goal<'tcx, Self>,
100 ) -> QueryResult<'tcx> {
103 ecx.infcx.probe(|_| {
104 let nested_obligations = tcx
105 .predicates_of(goal.predicate.def_id())
106 .instantiate(tcx, goal.predicate.trait_ref.substs);
107 ecx.evaluate_all_and_make_canonical_response(
108 nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)).collect(),
113 fn consider_builtin_sized_candidate(
114 ecx: &mut EvalCtxt<'_, 'tcx>,
115 goal: Goal<'tcx, Self>,
116 ) -> QueryResult<'tcx> {
117 ecx.probe_and_evaluate_goal_for_constituent_tys(
119 structural_traits::instantiate_constituent_tys_for_sized_trait,
123 fn consider_builtin_copy_clone_candidate(
124 ecx: &mut EvalCtxt<'_, 'tcx>,
125 goal: Goal<'tcx, Self>,
126 ) -> QueryResult<'tcx> {
127 ecx.probe_and_evaluate_goal_for_constituent_tys(
129 structural_traits::instantiate_constituent_tys_for_copy_clone_trait,
133 fn consider_builtin_pointer_sized_candidate(
134 ecx: &mut EvalCtxt<'_, 'tcx>,
135 goal: Goal<'tcx, Self>,
136 ) -> QueryResult<'tcx> {
137 if goal.predicate.self_ty().has_non_region_infer() {
138 return ecx.make_canonical_response(Certainty::AMBIGUOUS);
142 let self_ty = tcx.erase_regions(goal.predicate.self_ty());
144 if let Ok(layout) = tcx.layout_of(goal.param_env.and(self_ty))
145 && let usize_layout = tcx.layout_of(ty::ParamEnv::empty().and(tcx.types.usize)).unwrap().layout
146 && layout.layout.size() == usize_layout.size()
147 && layout.layout.align().abi == usize_layout.align().abi
149 // FIXME: We could make this faster by making a no-constraints response
150 ecx.make_canonical_response(Certainty::Yes)
156 fn consider_builtin_fn_trait_candidates(
157 ecx: &mut EvalCtxt<'_, 'tcx>,
158 goal: Goal<'tcx, Self>,
159 goal_kind: ty::ClosureKind,
160 ) -> QueryResult<'tcx> {
161 if let Some(tupled_inputs_and_output) =
162 structural_traits::extract_tupled_inputs_and_output_from_callable(
164 goal.predicate.self_ty(),
168 let pred = tupled_inputs_and_output
169 .map_bound(|(inputs, _)| {
171 .mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
173 .to_predicate(ecx.tcx());
174 Self::consider_assumption(ecx, goal, pred)
176 ecx.make_canonical_response(Certainty::AMBIGUOUS)
180 fn consider_builtin_tuple_candidate(
181 ecx: &mut EvalCtxt<'_, 'tcx>,
182 goal: Goal<'tcx, Self>,
183 ) -> QueryResult<'tcx> {
184 if let ty::Tuple(..) = goal.predicate.self_ty().kind() {
185 ecx.make_canonical_response(Certainty::Yes)
191 fn consider_builtin_pointee_candidate(
192 ecx: &mut EvalCtxt<'_, 'tcx>,
193 _goal: Goal<'tcx, Self>,
194 ) -> QueryResult<'tcx> {
195 ecx.make_canonical_response(Certainty::Yes)
198 fn consider_builtin_future_candidate(
199 ecx: &mut EvalCtxt<'_, 'tcx>,
200 goal: Goal<'tcx, Self>,
201 ) -> QueryResult<'tcx> {
202 let ty::Generator(def_id, _, _) = *goal.predicate.self_ty().kind() else {
203 return Err(NoSolution);
206 // Generators are not futures unless they come from `async` desugaring
208 if !tcx.generator_is_async(def_id) {
209 return Err(NoSolution);
212 // Async generator unconditionally implement `Future`
213 ecx.make_canonical_response(Certainty::Yes)
216 fn consider_builtin_generator_candidate(
217 ecx: &mut EvalCtxt<'_, 'tcx>,
218 goal: Goal<'tcx, Self>,
219 ) -> QueryResult<'tcx> {
220 let self_ty = goal.predicate.self_ty();
221 let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
222 return Err(NoSolution);
225 // `async`-desugared generators do not implement the generator trait
227 if tcx.generator_is_async(def_id) {
228 return Err(NoSolution);
231 let generator = substs.as_generator();
232 Self::consider_assumption(
236 tcx.mk_trait_ref(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
243 impl<'tcx> EvalCtxt<'_, 'tcx> {
244 /// Convenience function for traits that are structural, i.e. that only
245 /// have nested subgoals that only change the self type. Unlike other
246 /// evaluate-like helpers, this does a probe, so it doesn't need to be
248 fn probe_and_evaluate_goal_for_constituent_tys(
250 goal: Goal<'tcx, TraitPredicate<'tcx>>,
251 constituent_tys: impl Fn(&InferCtxt<'tcx>, Ty<'tcx>) -> Result<Vec<Ty<'tcx>>, NoSolution>,
252 ) -> QueryResult<'tcx> {
253 self.infcx.probe(|_| {
254 self.evaluate_all_and_make_canonical_response(
255 constituent_tys(self.infcx, goal.predicate.self_ty())?
260 ty::Binder::dummy(goal.predicate.with_self_ty(self.tcx(), ty)),
268 pub(super) fn compute_trait_goal(
270 goal: Goal<'tcx, TraitPredicate<'tcx>>,
271 ) -> QueryResult<'tcx> {
272 let candidates = self.assemble_and_evaluate_candidates(goal);
273 self.merge_trait_candidates_discard_reservation_impls(candidates)
276 #[instrument(level = "debug", skip(self), ret)]
277 pub(super) fn merge_trait_candidates_discard_reservation_impls(
279 mut candidates: Vec<Candidate<'tcx>>,
280 ) -> QueryResult<'tcx> {
281 match candidates.len() {
282 0 => return Err(NoSolution),
283 1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result),
287 if candidates.len() > 1 {
289 'outer: while i < candidates.len() {
290 for j in (0..candidates.len()).filter(|&j| i != j) {
291 if self.trait_candidate_should_be_dropped_in_favor_of(
295 debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
296 candidates.swap_remove(i);
301 debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
302 // If there are *STILL* multiple candidates, give up
303 // and report ambiguity.
306 debug!("multiple matches, ambig");
307 // FIXME: return overflow if all candidates overflow, otherwise return ambiguity.
313 Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result)
316 fn trait_candidate_should_be_dropped_in_favor_of(
318 candidate: &Candidate<'tcx>,
319 other: &Candidate<'tcx>,
321 // FIXME: implement this
322 match (candidate.source, other.source) {
323 (CandidateSource::Impl(_), _)
324 | (CandidateSource::ParamEnv(_), _)
325 | (CandidateSource::AliasBound, _)
326 | (CandidateSource::BuiltinImpl, _) => unimplemented!(),
330 fn discard_reservation_impl(&self, candidate: Candidate<'tcx>) -> Candidate<'tcx> {
331 if let CandidateSource::Impl(def_id) = candidate.source {
332 if let ty::ImplPolarity::Reservation = self.tcx().impl_polarity(def_id) {
333 debug!("Selected reservation impl");
334 // FIXME: reduce candidate to ambiguous
335 // FIXME: replace `var_values` with identity, yeet external constraints.