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::FxIndexSet;
11 use rustc_middle::ty::{self, Ty};
13 pub struct FulfillmentContext<'tcx> {
14 obligations: FxIndexSet<PredicateObligation<'tcx>>,
17 impl FulfillmentContext<'tcx> {
18 crate fn new() -> Self {
19 FulfillmentContext { obligations: FxIndexSet::default() }
23 impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
24 fn normalize_projection_type(
26 infcx: &InferCtxt<'_, 'tcx>,
27 _param_env: ty::ParamEnv<'tcx>,
28 projection_ty: ty::ProjectionTy<'tcx>,
29 _cause: ObligationCause<'tcx>,
31 infcx.tcx.mk_ty(ty::Projection(projection_ty))
34 fn register_predicate_obligation(
36 infcx: &InferCtxt<'_, 'tcx>,
37 obligation: PredicateObligation<'tcx>,
39 assert!(!infcx.is_in_snapshot());
40 let obligation = infcx.resolve_vars_if_possible(obligation);
42 self.obligations.insert(obligation);
45 fn select_all_or_error(
47 infcx: &InferCtxt<'_, 'tcx>,
48 ) -> Result<(), Vec<FulfillmentError<'tcx>>> {
49 self.select_where_possible(infcx)?;
51 if self.obligations.is_empty() {
57 .map(|obligation| FulfillmentError {
58 obligation: obligation.clone(),
59 code: FulfillmentErrorCode::CodeAmbiguity,
60 points_at_arg_span: false,
61 // FIXME - does Chalk have a notation of 'root obligation'?
62 // This is just for diagnostics, so it's okay if this is wrong
63 root_obligation: obligation.clone(),
70 fn select_where_possible(
72 infcx: &InferCtxt<'_, 'tcx>,
73 ) -> Result<(), Vec<FulfillmentError<'tcx>>> {
74 assert!(!infcx.is_in_snapshot());
76 let mut errors = Vec::new();
77 let mut next_round = FxIndexSet::default();
78 let mut making_progress;
81 making_progress = false;
83 // We iterate over all obligations, and record if we are able
84 // to unambiguously prove at least one obligation.
85 for obligation in self.obligations.drain(..) {
86 let obligation = infcx.resolve_vars_if_possible(obligation);
87 let environment = obligation.param_env.caller_bounds();
88 let goal = ChalkEnvironmentAndGoal { environment, goal: obligation.predicate };
89 let mut orig_values = OriginalQueryValues::default();
90 let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values);
92 match infcx.tcx.evaluate_goal(canonical_goal) {
94 if response.is_proven() {
95 making_progress = true;
97 match infcx.instantiate_query_response_and_region_obligations(
103 Ok(infer_ok) => next_round.extend(
104 infer_ok.obligations.into_iter().map(|obligation| {
105 assert!(!infcx.is_in_snapshot());
106 infcx.resolve_vars_if_possible(obligation)
110 Err(_err) => errors.push(FulfillmentError {
111 obligation: obligation.clone(),
112 code: FulfillmentErrorCode::CodeSelectionError(
113 SelectionError::Unimplemented,
115 points_at_arg_span: false,
116 // FIXME - does Chalk have a notation of 'root obligation'?
117 // This is just for diagnostics, so it's okay if this is wrong
118 root_obligation: obligation,
122 // Ambiguous: retry at next round.
123 next_round.insert(obligation);
127 Err(NoSolution) => errors.push(FulfillmentError {
128 obligation: obligation.clone(),
129 code: FulfillmentErrorCode::CodeSelectionError(
130 SelectionError::Unimplemented,
132 points_at_arg_span: false,
133 // FIXME - does Chalk have a notation of 'root obligation'?
134 // This is just for diagnostics, so it's okay if this is wrong
135 root_obligation: obligation,
139 next_round = std::mem::replace(&mut self.obligations, next_round);
141 if !making_progress {
146 if errors.is_empty() { Ok(()) } else { Err(errors) }
149 fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
150 self.obligations.iter().cloned().collect()