1 //! Defines a Chalk-based `TraitEngine`
3 use crate::infer::canonical::OriginalQueryValues;
4 use crate::infer::InferCtxt;
5 use crate::traits::query::NoSolution;
7 ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, ObligationCause,
8 PredicateObligation, SelectionError, TraitEngine,
10 use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
11 use rustc_middle::ty::{self, Ty};
13 pub struct FulfillmentContext<'tcx> {
14 obligations: FxIndexSet<PredicateObligation<'tcx>>,
16 relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>,
19 impl FulfillmentContext<'tcx> {
20 crate fn new() -> Self {
22 obligations: FxIndexSet::default(),
23 relationships: FxHashMap::default(),
28 impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
29 fn normalize_projection_type(
31 infcx: &InferCtxt<'_, 'tcx>,
32 _param_env: ty::ParamEnv<'tcx>,
33 projection_ty: ty::ProjectionTy<'tcx>,
34 _cause: ObligationCause<'tcx>,
36 infcx.tcx.mk_ty(ty::Projection(projection_ty))
39 fn register_predicate_obligation(
41 infcx: &InferCtxt<'_, 'tcx>,
42 obligation: PredicateObligation<'tcx>,
44 assert!(!infcx.is_in_snapshot());
45 let obligation = infcx.resolve_vars_if_possible(obligation);
47 super::relationships::update(self, infcx, &obligation);
49 self.obligations.insert(obligation);
52 fn select_all_or_error(
54 infcx: &InferCtxt<'_, 'tcx>,
55 ) -> Vec<FulfillmentError<'tcx>> {
57 let errors = self.select_where_possible(infcx);
59 if !errors.is_empty() {
64 // any remaining obligations are errors
68 .map(|obligation| FulfillmentError {
69 obligation: obligation.clone(),
70 code: FulfillmentErrorCode::CodeAmbiguity,
71 // FIXME - does Chalk have a notation of 'root obligation'?
72 // This is just for diagnostics, so it's okay if this is wrong
73 root_obligation: obligation.clone(),
78 fn select_where_possible(
80 infcx: &InferCtxt<'_, 'tcx>,
81 ) -> Vec<FulfillmentError<'tcx>> {
82 assert!(!infcx.is_in_snapshot());
84 let mut errors = Vec::new();
85 let mut next_round = FxIndexSet::default();
86 let mut making_progress;
89 making_progress = false;
91 // We iterate over all obligations, and record if we are able
92 // to unambiguously prove at least one obligation.
93 for obligation in self.obligations.drain(..) {
94 let obligation = infcx.resolve_vars_if_possible(obligation);
95 let environment = obligation.param_env.caller_bounds();
96 let goal = ChalkEnvironmentAndGoal { environment, goal: obligation.predicate };
97 let mut orig_values = OriginalQueryValues::default();
98 let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values);
100 match infcx.tcx.evaluate_goal(canonical_goal) {
102 if response.is_proven() {
103 making_progress = true;
105 match infcx.instantiate_query_response_and_region_obligations(
107 obligation.param_env,
111 Ok(infer_ok) => next_round.extend(
112 infer_ok.obligations.into_iter().map(|obligation| {
113 assert!(!infcx.is_in_snapshot());
114 infcx.resolve_vars_if_possible(obligation)
118 Err(_err) => errors.push(FulfillmentError {
119 obligation: obligation.clone(),
120 code: FulfillmentErrorCode::CodeSelectionError(
121 SelectionError::Unimplemented,
123 // FIXME - does Chalk have a notation of 'root obligation'?
124 // This is just for diagnostics, so it's okay if this is wrong
125 root_obligation: obligation,
129 // Ambiguous: retry at next round.
130 next_round.insert(obligation);
134 Err(NoSolution) => errors.push(FulfillmentError {
135 obligation: obligation.clone(),
136 code: FulfillmentErrorCode::CodeSelectionError(
137 SelectionError::Unimplemented,
139 // FIXME - does Chalk have a notation of 'root obligation'?
140 // This is just for diagnostics, so it's okay if this is wrong
141 root_obligation: obligation,
145 next_round = std::mem::replace(&mut self.obligations, next_round);
147 if !making_progress {
155 fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
156 self.obligations.iter().cloned().collect()
159 fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
160 &mut self.relationships