1 use crate::infer::canonical::{Canonical, OriginalQueryValues};
2 use crate::infer::InferCtxt;
3 use crate::traits::query::NoSolution;
5 Environment, FulfillmentError, FulfillmentErrorCode, InEnvironment, ObligationCause,
6 PredicateObligation, SelectionError, TraitEngine,
8 use crate::ty::{self, Ty};
9 use rustc_data_structures::fx::FxHashSet;
11 pub type CanonicalGoal<'tcx> = Canonical<'tcx, InEnvironment<'tcx, ty::Predicate<'tcx>>>;
13 pub struct FulfillmentContext<'tcx> {
14 obligations: FxHashSet<InEnvironment<'tcx, PredicateObligation<'tcx>>>,
17 impl FulfillmentContext<'tcx> {
18 crate fn new() -> Self {
19 FulfillmentContext { obligations: FxHashSet::default() }
24 infcx: &InferCtxt<'_, 'tcx>,
25 obligation: PredicateObligation<'tcx>,
26 ) -> InEnvironment<'tcx, PredicateObligation<'tcx>> {
27 assert!(!infcx.is_in_snapshot());
28 let obligation = infcx.resolve_vars_if_possible(&obligation);
30 let environment = match obligation.param_env.def_id {
31 Some(def_id) => infcx.tcx.environment(def_id),
32 None if obligation.param_env.caller_bounds.is_empty() => {
33 Environment { clauses: ty::List::empty() }
35 _ => bug!("non-empty `ParamEnv` with no def-id"),
38 InEnvironment { environment, goal: obligation }
41 impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
42 fn normalize_projection_type(
44 infcx: &InferCtxt<'_, 'tcx>,
45 _param_env: ty::ParamEnv<'tcx>,
46 projection_ty: ty::ProjectionTy<'tcx>,
47 _cause: ObligationCause<'tcx>,
49 infcx.tcx.mk_ty(ty::Projection(projection_ty))
52 fn register_predicate_obligation(
54 infcx: &InferCtxt<'_, 'tcx>,
55 obligation: PredicateObligation<'tcx>,
57 self.obligations.insert(in_environment(infcx, obligation));
60 fn select_all_or_error(
62 infcx: &InferCtxt<'_, 'tcx>,
63 ) -> Result<(), Vec<FulfillmentError<'tcx>>> {
64 self.select_where_possible(infcx)?;
66 if self.obligations.is_empty() {
72 .map(|obligation| FulfillmentError {
73 obligation: obligation.goal.clone(),
74 code: FulfillmentErrorCode::CodeAmbiguity,
75 points_at_arg_span: false,
82 fn select_where_possible(
84 infcx: &InferCtxt<'_, 'tcx>,
85 ) -> Result<(), Vec<FulfillmentError<'tcx>>> {
86 let mut errors = Vec::new();
87 let mut next_round = FxHashSet::default();
88 let mut making_progress;
91 making_progress = false;
93 // We iterate over all obligations, and record if we are able
94 // to unambiguously prove at least one obligation.
95 for obligation in self.obligations.drain() {
96 let mut orig_values = OriginalQueryValues::default();
97 let canonical_goal = infcx.canonicalize_query(
99 environment: obligation.environment,
100 goal: obligation.goal.predicate,
105 match infcx.tcx.evaluate_goal(canonical_goal) {
107 if response.is_proven() {
108 making_progress = true;
110 match infcx.instantiate_query_response_and_region_obligations(
111 &obligation.goal.cause,
112 obligation.goal.param_env,
116 Ok(infer_ok) => next_round.extend(
120 .map(|obligation| in_environment(infcx, obligation)),
123 Err(_err) => errors.push(FulfillmentError {
124 obligation: obligation.goal,
125 code: FulfillmentErrorCode::CodeSelectionError(
126 SelectionError::Unimplemented,
128 points_at_arg_span: false,
132 // Ambiguous: retry at next round.
133 next_round.insert(obligation);
137 Err(NoSolution) => errors.push(FulfillmentError {
138 obligation: obligation.goal,
139 code: FulfillmentErrorCode::CodeSelectionError(
140 SelectionError::Unimplemented,
142 points_at_arg_span: false,
146 next_round = std::mem::replace(&mut self.obligations, next_round);
148 if !making_progress {
153 if errors.is_empty() { Ok(()) } else { Err(errors) }
156 fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
157 self.obligations.iter().map(|obligation| obligation.goal.clone()).collect()