| use crate::error_reporting::traits::TypeErrCtxtOverflowExt; |
| use crate::infer::{InferCtxt, TyOrConstInferVar}; |
| use crate::traits::normalize::normalize_with_depth_to; |
| use rustc_data_structures::captures::Captures; |
| use rustc_data_structures::obligation_forest::ProcessResult; |
| use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome}; |
| use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; |
| use rustc_infer::infer::DefineOpaqueTypes; |
| use rustc_infer::traits::{FromSolverError, ProjectionCacheKey}; |
| use rustc_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine}; |
| use rustc_middle::bug; |
| use rustc_middle::mir::interpret::ErrorHandled; |
| use rustc_middle::ty::abstract_const::NotConstEvaluatable; |
| use rustc_middle::ty::error::{ExpectedFound, TypeError}; |
| use rustc_middle::ty::GenericArgsRef; |
| use rustc_middle::ty::{self, Binder, Const, TypeVisitableExt}; |
| use std::marker::PhantomData; |
| |
| use super::project::{self, ProjectAndUnifyResult}; |
| use super::select::SelectionContext; |
| use super::wf; |
| use super::EvaluationResult; |
| use super::PredicateObligation; |
| use super::Unimplemented; |
| use super::{const_evaluatable, ScrubbedTraitError}; |
| use super::{FulfillmentError, FulfillmentErrorCode}; |
| |
| use crate::traits::project::PolyProjectionObligation; |
| use crate::traits::project::ProjectionCacheKeyExt as _; |
| use crate::traits::query::evaluate_obligation::InferCtxtExt; |
| |
| impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> { |
| /// Note that we include both the `ParamEnv` and the `Predicate`, |
| /// as the `ParamEnv` can influence whether fulfillment succeeds |
| /// or fails. |
| type CacheKey = ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>; |
| |
| fn as_cache_key(&self) -> Self::CacheKey { |
| self.obligation.param_env.and(self.obligation.predicate) |
| } |
| } |
| |
| /// The fulfillment context is used to drive trait resolution. It |
| /// consists of a list of obligations that must be (eventually) |
| /// satisfied. The job is to track which are satisfied, which yielded |
| /// errors, and which are still pending. At any point, users can call |
| /// `select_where_possible`, and the fulfillment context will try to do |
| /// selection, retaining only those obligations that remain |
| /// ambiguous. This may be helpful in pushing type inference |
| /// along. Once all type inference constraints have been generated, the |
| /// method `select_all_or_error` can be used to report any remaining |
| /// ambiguous cases as errors. |
| pub struct FulfillmentContext<'tcx, E: 'tcx> { |
| /// A list of all obligations that have been registered with this |
| /// fulfillment context. |
| predicates: ObligationForest<PendingPredicateObligation<'tcx>>, |
| |
| /// The snapshot in which this context was created. Using the context |
| /// outside of this snapshot leads to subtle bugs if the snapshot |
| /// gets rolled back. Because of this we explicitly check that we only |
| /// use the context in exactly this snapshot. |
| usable_in_snapshot: usize, |
| |
| _errors: PhantomData<E>, |
| } |
| |
| #[derive(Clone, Debug)] |
| pub struct PendingPredicateObligation<'tcx> { |
| pub obligation: PredicateObligation<'tcx>, |
| // This is far more often read than modified, meaning that we |
| // should mostly optimize for reading speed, while modifying is not as relevant. |
| // |
| // For whatever reason using a boxed slice is slower than using a `Vec` here. |
| pub stalled_on: Vec<TyOrConstInferVar>, |
| } |
| |
| // `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. |
| #[cfg(target_pointer_width = "64")] |
| rustc_data_structures::static_assert_size!(PendingPredicateObligation<'_>, 72); |
| |
| impl<'tcx, E> FulfillmentContext<'tcx, E> |
| where |
| E: FromSolverError<'tcx, OldSolverError<'tcx>>, |
| { |
| /// Creates a new fulfillment context. |
| pub(super) fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentContext<'tcx, E> { |
| assert!( |
| !infcx.next_trait_solver(), |
| "old trait solver fulfillment context created when \ |
| infcx is set up for new trait solver" |
| ); |
| FulfillmentContext { |
| predicates: ObligationForest::new(), |
| usable_in_snapshot: infcx.num_open_snapshots(), |
| _errors: PhantomData, |
| } |
| } |
| |
| /// Attempts to select obligations using `selcx`. |
| fn select(&mut self, selcx: SelectionContext<'_, 'tcx>) -> Vec<E> { |
| let span = debug_span!("select", obligation_forest_size = ?self.predicates.len()); |
| let _enter = span.enter(); |
| let infcx = selcx.infcx; |
| |
| // Process pending obligations. |
| let outcome: Outcome<_, _> = |
| self.predicates.process_obligations(&mut FulfillProcessor { selcx }); |
| |
| // FIXME: if we kept the original cache key, we could mark projection |
| // obligations as complete for the projection cache here. |
| |
| let errors: Vec<E> = outcome |
| .errors |
| .into_iter() |
| .map(|err| E::from_solver_error(infcx, OldSolverError(err))) |
| .collect(); |
| |
| debug!( |
| "select({} predicates remaining, {} errors) done", |
| self.predicates.len(), |
| errors.len() |
| ); |
| |
| errors |
| } |
| } |
| |
| impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentContext<'tcx, E> |
| where |
| E: FromSolverError<'tcx, OldSolverError<'tcx>>, |
| { |
| #[inline] |
| fn register_predicate_obligation( |
| &mut self, |
| infcx: &InferCtxt<'tcx>, |
| mut obligation: PredicateObligation<'tcx>, |
| ) { |
| assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); |
| // this helps to reduce duplicate errors, as well as making |
| // debug output much nicer to read and so on. |
| debug_assert!(!obligation.param_env.has_non_region_infer()); |
| obligation.predicate = infcx.resolve_vars_if_possible(obligation.predicate); |
| |
| debug!(?obligation, "register_predicate_obligation"); |
| |
| self.predicates |
| .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] }); |
| } |
| |
| fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> { |
| self.predicates |
| .to_errors(FulfillmentErrorCode::Ambiguity { overflow: None }) |
| .into_iter() |
| .map(|err| E::from_solver_error(infcx, OldSolverError(err))) |
| .collect() |
| } |
| |
| fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> { |
| let selcx = SelectionContext::new(infcx); |
| self.select(selcx) |
| } |
| |
| fn drain_unstalled_obligations( |
| &mut self, |
| infcx: &InferCtxt<'tcx>, |
| ) -> Vec<PredicateObligation<'tcx>> { |
| let mut processor = DrainProcessor { removed_predicates: Vec::new(), infcx }; |
| let outcome: Outcome<_, _> = self.predicates.process_obligations(&mut processor); |
| assert!(outcome.errors.is_empty()); |
| return processor.removed_predicates; |
| |
| struct DrainProcessor<'a, 'tcx> { |
| infcx: &'a InferCtxt<'tcx>, |
| removed_predicates: Vec<PredicateObligation<'tcx>>, |
| } |
| |
| impl<'tcx> ObligationProcessor for DrainProcessor<'_, 'tcx> { |
| type Obligation = PendingPredicateObligation<'tcx>; |
| type Error = !; |
| type OUT = Outcome<Self::Obligation, Self::Error>; |
| |
| fn needs_process_obligation(&self, pending_obligation: &Self::Obligation) -> bool { |
| pending_obligation |
| .stalled_on |
| .iter() |
| .any(|&var| self.infcx.ty_or_const_infer_var_changed(var)) |
| } |
| |
| fn process_obligation( |
| &mut self, |
| pending_obligation: &mut PendingPredicateObligation<'tcx>, |
| ) -> ProcessResult<PendingPredicateObligation<'tcx>, !> { |
| assert!(self.needs_process_obligation(pending_obligation)); |
| self.removed_predicates.push(pending_obligation.obligation.clone()); |
| ProcessResult::Changed(vec![]) |
| } |
| |
| fn process_backedge<'c, I>( |
| &mut self, |
| cycle: I, |
| _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>, |
| ) -> Result<(), !> |
| where |
| I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>, |
| { |
| self.removed_predicates.extend(cycle.map(|c| c.obligation.clone())); |
| Ok(()) |
| } |
| } |
| } |
| |
| fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> { |
| self.predicates.map_pending_obligations(|o| o.obligation.clone()) |
| } |
| } |
| |
| struct FulfillProcessor<'a, 'tcx> { |
| selcx: SelectionContext<'a, 'tcx>, |
| } |
| |
| fn mk_pending(os: Vec<PredicateObligation<'_>>) -> Vec<PendingPredicateObligation<'_>> { |
| os.into_iter() |
| .map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] }) |
| .collect() |
| } |
| |
| impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { |
| type Obligation = PendingPredicateObligation<'tcx>; |
| type Error = FulfillmentErrorCode<'tcx>; |
| type OUT = Outcome<Self::Obligation, Self::Error>; |
| |
| /// Compared to `needs_process_obligation` this and its callees |
| /// contain some optimizations that come at the price of false negatives. |
| /// |
| /// They |
| /// - reduce branching by covering only the most common case |
| /// - take a read-only view of the unification tables which allows skipping undo_log |
| /// construction. |
| /// - bail out on value-cache misses in ena to avoid pointer chasing |
| /// - hoist RefCell locking out of the loop |
| #[inline] |
| fn skippable_obligations<'b>( |
| &'b self, |
| it: impl Iterator<Item = &'b Self::Obligation>, |
| ) -> usize { |
| let is_unchanged = self.selcx.infcx.is_ty_infer_var_definitely_unchanged(); |
| |
| it.take_while(|o| match o.stalled_on.as_slice() { |
| [o] => is_unchanged(*o), |
| _ => false, |
| }) |
| .count() |
| } |
| |
| /// Identifies whether a predicate obligation needs processing. |
| /// |
| /// This is always inlined because it has a single callsite and it is |
| /// called *very* frequently. Be careful modifying this code! Several |
| /// compile-time benchmarks are very sensitive to even small changes. |
| #[inline(always)] |
| fn needs_process_obligation(&self, pending_obligation: &Self::Obligation) -> bool { |
| // If we were stalled on some unresolved variables, first check whether |
| // any of them have been resolved; if not, don't bother doing more work |
| // yet. |
| let stalled_on = &pending_obligation.stalled_on; |
| match stalled_on.len() { |
| // This case is the hottest most of the time, being hit up to 99% |
| // of the time. `keccak` and `cranelift-codegen-0.82.1` are |
| // benchmarks that particularly stress this path. |
| 1 => self.selcx.infcx.ty_or_const_infer_var_changed(stalled_on[0]), |
| |
| // In this case we haven't changed, but wish to make a change. Note |
| // that this is a special case, and is not equivalent to the `_` |
| // case below, which would return `false` for an empty `stalled_on` |
| // vector. |
| // |
| // This case is usually hit only 1% of the time or less, though it |
| // reaches 20% in `wasmparser-0.101.0`. |
| 0 => true, |
| |
| // This case is usually hit only 1% of the time or less, though it |
| // reaches 95% in `mime-0.3.16`, 64% in `wast-54.0.0`, and 12% in |
| // `inflate-0.4.5`. |
| // |
| // The obvious way of writing this, with a call to `any()` and no |
| // closure, is currently slower than this version. |
| _ => (|| { |
| for &infer_var in stalled_on { |
| if self.selcx.infcx.ty_or_const_infer_var_changed(infer_var) { |
| return true; |
| } |
| } |
| false |
| })(), |
| } |
| } |
| |
| /// Processes a predicate obligation and returns either: |
| /// - `Changed(v)` if the predicate is true, presuming that `v` are also true |
| /// - `Unchanged` if we don't have enough info to be sure |
| /// - `Error(e)` if the predicate does not hold |
| /// |
| /// This is called much less often than `needs_process_obligation`, so we |
| /// never inline it. |
| #[inline(never)] |
| #[instrument(level = "debug", skip(self, pending_obligation))] |
| fn process_obligation( |
| &mut self, |
| pending_obligation: &mut PendingPredicateObligation<'tcx>, |
| ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> { |
| pending_obligation.stalled_on.truncate(0); |
| |
| let obligation = &mut pending_obligation.obligation; |
| |
| debug!(?obligation, "pre-resolve"); |
| |
| if obligation.predicate.has_non_region_infer() { |
| obligation.predicate = self.selcx.infcx.resolve_vars_if_possible(obligation.predicate); |
| } |
| |
| let obligation = &pending_obligation.obligation; |
| |
| let infcx = self.selcx.infcx; |
| |
| if obligation.predicate.has_aliases() { |
| let mut obligations = Vec::new(); |
| let predicate = normalize_with_depth_to( |
| &mut self.selcx, |
| obligation.param_env, |
| obligation.cause.clone(), |
| obligation.recursion_depth + 1, |
| obligation.predicate, |
| &mut obligations, |
| ); |
| if predicate != obligation.predicate { |
| obligations.push(obligation.with(infcx.tcx, predicate)); |
| return ProcessResult::Changed(mk_pending(obligations)); |
| } |
| } |
| let binder = obligation.predicate.kind(); |
| match binder.no_bound_vars() { |
| None => match binder.skip_binder() { |
| // Evaluation will discard candidates using the leak check. |
| // This means we need to pass it the bound version of our |
| // predicate. |
| ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) => { |
| let trait_obligation = obligation.with(infcx.tcx, binder.rebind(trait_ref)); |
| |
| self.process_trait_obligation( |
| obligation, |
| trait_obligation, |
| &mut pending_obligation.stalled_on, |
| ) |
| } |
| ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { |
| let project_obligation = obligation.with(infcx.tcx, binder.rebind(data)); |
| |
| self.process_projection_obligation( |
| obligation, |
| project_obligation, |
| &mut pending_obligation.stalled_on, |
| ) |
| } |
| ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(_)) |
| | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(_)) |
| | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) |
| | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) |
| | ty::PredicateKind::ObjectSafe(_) |
| | ty::PredicateKind::Subtype(_) |
| | ty::PredicateKind::Coerce(_) |
| | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) |
| | ty::PredicateKind::ConstEquate(..) => { |
| let pred = ty::Binder::dummy(infcx.enter_forall_and_leak_universe(binder)); |
| ProcessResult::Changed(mk_pending(vec![obligation.with(infcx.tcx, pred)])) |
| } |
| ty::PredicateKind::Ambiguous => ProcessResult::Unchanged, |
| ty::PredicateKind::NormalizesTo(..) => { |
| bug!("NormalizesTo is only used by the new solver") |
| } |
| ty::PredicateKind::AliasRelate(..) => { |
| bug!("AliasRelate is only used by the new solver") |
| } |
| }, |
| Some(pred) => match pred { |
| ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { |
| let trait_obligation = obligation.with(infcx.tcx, Binder::dummy(data)); |
| |
| self.process_trait_obligation( |
| obligation, |
| trait_obligation, |
| &mut pending_obligation.stalled_on, |
| ) |
| } |
| |
| ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(data)) => { |
| if infcx.considering_regions { |
| infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)); |
| } |
| |
| ProcessResult::Changed(vec![]) |
| } |
| |
| ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( |
| t_a, |
| r_b, |
| ))) => { |
| if infcx.considering_regions { |
| infcx.register_region_obligation_with_cause(t_a, r_b, &obligation.cause); |
| } |
| ProcessResult::Changed(vec![]) |
| } |
| |
| ty::PredicateKind::Clause(ty::ClauseKind::Projection(ref data)) => { |
| let project_obligation = obligation.with(infcx.tcx, Binder::dummy(*data)); |
| |
| self.process_projection_obligation( |
| obligation, |
| project_obligation, |
| &mut pending_obligation.stalled_on, |
| ) |
| } |
| |
| ty::PredicateKind::ObjectSafe(trait_def_id) => { |
| if !self.selcx.tcx().is_object_safe(trait_def_id) { |
| ProcessResult::Error(FulfillmentErrorCode::Select(Unimplemented)) |
| } else { |
| ProcessResult::Changed(vec![]) |
| } |
| } |
| |
| ty::PredicateKind::Ambiguous => ProcessResult::Unchanged, |
| ty::PredicateKind::NormalizesTo(..) => { |
| bug!("NormalizesTo is only used by the new solver") |
| } |
| ty::PredicateKind::AliasRelate(..) => { |
| bug!("AliasRelate is only used by the new solver") |
| } |
| // Compute `ConstArgHasType` above the overflow check below. |
| // This is because this is not ever a useful obligation to report |
| // as the cause of an overflow. |
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { |
| let ct = infcx.shallow_resolve_const(ct); |
| let ct_ty = match ct.kind() { |
| ty::ConstKind::Infer(var) => { |
| let var = match var { |
| ty::InferConst::Var(vid) => TyOrConstInferVar::Const(vid), |
| ty::InferConst::EffectVar(vid) => TyOrConstInferVar::Effect(vid), |
| ty::InferConst::Fresh(_) => { |
| bug!("encountered fresh const in fulfill") |
| } |
| }; |
| pending_obligation.stalled_on.clear(); |
| pending_obligation.stalled_on.extend([var]); |
| return ProcessResult::Unchanged; |
| } |
| ty::ConstKind::Error(_) => return ProcessResult::Changed(vec![]), |
| ty::ConstKind::Value(ty, _) => ty, |
| ty::ConstKind::Unevaluated(uv) => { |
| infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args) |
| } |
| // FIXME(generic_const_exprs): we should construct an alias like |
| // `<lhs_ty as Add<rhs_ty>>::Output` when this is an `Expr` representing |
| // `lhs + rhs`. |
| ty::ConstKind::Expr(_) => { |
| return ProcessResult::Changed(mk_pending(vec![])); |
| } |
| ty::ConstKind::Placeholder(_) => { |
| bug!("placeholder const {:?} in old solver", ct) |
| } |
| ty::ConstKind::Bound(_, _) => bug!("escaping bound vars in {:?}", ct), |
| ty::ConstKind::Param(param_ct) => { |
| param_ct.find_ty_from_env(obligation.param_env) |
| } |
| }; |
| |
| match infcx.at(&obligation.cause, obligation.param_env).eq( |
| // Only really excercised by generic_const_exprs |
| DefineOpaqueTypes::Yes, |
| ct_ty, |
| ty, |
| ) { |
| Ok(inf_ok) => ProcessResult::Changed(mk_pending(inf_ok.into_obligations())), |
| Err(_) => ProcessResult::Error(FulfillmentErrorCode::Select( |
| SelectionError::ConstArgHasWrongType { ct, ct_ty, expected_ty: ty }, |
| )), |
| } |
| } |
| |
| // General case overflow check. Allow `process_trait_obligation` |
| // and `process_projection_obligation` to handle checking for |
| // the recursion limit themselves. Also don't check some |
| // predicate kinds that don't give further obligations. |
| _ if !self |
| .selcx |
| .tcx() |
| .recursion_limit() |
| .value_within_limit(obligation.recursion_depth) => |
| { |
| self.selcx.infcx.err_ctxt().report_overflow_obligation(&obligation, false); |
| } |
| |
| ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { |
| match wf::obligations( |
| self.selcx.infcx, |
| obligation.param_env, |
| obligation.cause.body_id, |
| obligation.recursion_depth + 1, |
| arg, |
| obligation.cause.span, |
| ) { |
| None => { |
| pending_obligation.stalled_on = |
| vec![TyOrConstInferVar::maybe_from_generic_arg(arg).unwrap()]; |
| ProcessResult::Unchanged |
| } |
| Some(os) => ProcessResult::Changed(mk_pending(os)), |
| } |
| } |
| |
| ty::PredicateKind::Subtype(subtype) => { |
| match self.selcx.infcx.subtype_predicate( |
| &obligation.cause, |
| obligation.param_env, |
| Binder::dummy(subtype), |
| ) { |
| Err((a, b)) => { |
| // None means that both are unresolved. |
| pending_obligation.stalled_on = |
| vec![TyOrConstInferVar::Ty(a), TyOrConstInferVar::Ty(b)]; |
| ProcessResult::Unchanged |
| } |
| Ok(Ok(mut ok)) => { |
| for subobligation in &mut ok.obligations { |
| subobligation.set_depth_from_parent(obligation.recursion_depth); |
| } |
| ProcessResult::Changed(mk_pending(ok.obligations)) |
| } |
| Ok(Err(err)) => { |
| let expected_found = |
| ExpectedFound::new(subtype.a_is_expected, subtype.a, subtype.b); |
| ProcessResult::Error(FulfillmentErrorCode::Subtype(expected_found, err)) |
| } |
| } |
| } |
| |
| ty::PredicateKind::Coerce(coerce) => { |
| match self.selcx.infcx.coerce_predicate( |
| &obligation.cause, |
| obligation.param_env, |
| Binder::dummy(coerce), |
| ) { |
| Err((a, b)) => { |
| // None means that both are unresolved. |
| pending_obligation.stalled_on = |
| vec![TyOrConstInferVar::Ty(a), TyOrConstInferVar::Ty(b)]; |
| ProcessResult::Unchanged |
| } |
| Ok(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), |
| Ok(Err(err)) => { |
| let expected_found = ExpectedFound::new(false, coerce.a, coerce.b); |
| ProcessResult::Error(FulfillmentErrorCode::Subtype(expected_found, err)) |
| } |
| } |
| } |
| |
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { |
| match const_evaluatable::is_const_evaluatable( |
| self.selcx.infcx, |
| uv, |
| obligation.param_env, |
| obligation.cause.span, |
| ) { |
| Ok(()) => ProcessResult::Changed(vec![]), |
| Err(NotConstEvaluatable::MentionsInfer) => { |
| pending_obligation.stalled_on.clear(); |
| pending_obligation.stalled_on.extend( |
| uv.walk().filter_map(TyOrConstInferVar::maybe_from_generic_arg), |
| ); |
| ProcessResult::Unchanged |
| } |
| Err( |
| e @ NotConstEvaluatable::MentionsParam |
| | e @ NotConstEvaluatable::Error(_), |
| ) => ProcessResult::Error(FulfillmentErrorCode::Select( |
| SelectionError::NotConstEvaluatable(e), |
| )), |
| } |
| } |
| |
| ty::PredicateKind::ConstEquate(c1, c2) => { |
| let tcx = self.selcx.tcx(); |
| assert!( |
| tcx.features().generic_const_exprs, |
| "`ConstEquate` without a feature gate: {c1:?} {c2:?}", |
| ); |
| // FIXME: we probably should only try to unify abstract constants |
| // if the constants depend on generic parameters. |
| // |
| // Let's just see where this breaks :shrug: |
| { |
| let c1 = tcx.expand_abstract_consts(c1); |
| let c2 = tcx.expand_abstract_consts(c2); |
| debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2); |
| |
| use rustc_hir::def::DefKind; |
| use ty::Unevaluated; |
| match (c1.kind(), c2.kind()) { |
| (Unevaluated(a), Unevaluated(b)) |
| if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst => |
| { |
| if let Ok(new_obligations) = infcx |
| .at(&obligation.cause, obligation.param_env) |
| // Can define opaque types as this is only reachable with |
| // `generic_const_exprs` |
| .eq( |
| DefineOpaqueTypes::Yes, |
| ty::AliasTerm::from(a), |
| ty::AliasTerm::from(b), |
| ) |
| { |
| return ProcessResult::Changed(mk_pending( |
| new_obligations.into_obligations(), |
| )); |
| } |
| } |
| (_, Unevaluated(_)) | (Unevaluated(_), _) => (), |
| (_, _) => { |
| if let Ok(new_obligations) = infcx |
| .at(&obligation.cause, obligation.param_env) |
| // Can define opaque types as this is only reachable with |
| // `generic_const_exprs` |
| .eq(DefineOpaqueTypes::Yes, c1, c2) |
| { |
| return ProcessResult::Changed(mk_pending( |
| new_obligations.into_obligations(), |
| )); |
| } |
| } |
| } |
| } |
| |
| let stalled_on = &mut pending_obligation.stalled_on; |
| |
| let mut evaluate = |c: Const<'tcx>| { |
| if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { |
| match self.selcx.infcx.try_const_eval_resolve( |
| obligation.param_env, |
| unevaluated, |
| obligation.cause.span, |
| ) { |
| Ok(val) => Ok(val), |
| Err(e) => { |
| match e { |
| ErrorHandled::TooGeneric(..) => { |
| stalled_on.extend(unevaluated.args.iter().filter_map( |
| TyOrConstInferVar::maybe_from_generic_arg, |
| )); |
| } |
| _ => {} |
| } |
| Err(e) |
| } |
| } |
| } else { |
| Ok(c) |
| } |
| }; |
| |
| match (evaluate(c1), evaluate(c2)) { |
| (Ok(c1), Ok(c2)) => { |
| match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq( |
| // Can define opaque types as this is only reachable with |
| // `generic_const_exprs` |
| DefineOpaqueTypes::Yes, |
| c1, |
| c2, |
| ) { |
| Ok(inf_ok) => { |
| ProcessResult::Changed(mk_pending(inf_ok.into_obligations())) |
| } |
| Err(err) => { |
| ProcessResult::Error(FulfillmentErrorCode::ConstEquate( |
| ExpectedFound::new(true, c1, c2), |
| err, |
| )) |
| } |
| } |
| } |
| (Err(ErrorHandled::Reported(reported, _)), _) |
| | (_, Err(ErrorHandled::Reported(reported, _))) => ProcessResult::Error( |
| FulfillmentErrorCode::Select(SelectionError::NotConstEvaluatable( |
| NotConstEvaluatable::Error(reported.into()), |
| )), |
| ), |
| (Err(ErrorHandled::TooGeneric(_)), _) |
| | (_, Err(ErrorHandled::TooGeneric(_))) => { |
| if c1.has_non_region_infer() || c2.has_non_region_infer() { |
| ProcessResult::Unchanged |
| } else { |
| // Two different constants using generic parameters ~> error. |
| let expected_found = ExpectedFound::new(true, c1, c2); |
| ProcessResult::Error(FulfillmentErrorCode::ConstEquate( |
| expected_found, |
| TypeError::ConstMismatch(expected_found), |
| )) |
| } |
| } |
| } |
| } |
| }, |
| } |
| } |
| |
| #[inline(never)] |
| fn process_backedge<'c, I>( |
| &mut self, |
| cycle: I, |
| _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>, |
| ) -> Result<(), FulfillmentErrorCode<'tcx>> |
| where |
| I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>, |
| { |
| if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) { |
| debug!("process_child_obligations: coinductive match"); |
| Ok(()) |
| } else { |
| let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect(); |
| Err(FulfillmentErrorCode::Cycle(cycle)) |
| } |
| } |
| } |
| |
| impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { |
| #[instrument(level = "debug", skip(self, obligation, stalled_on))] |
| fn process_trait_obligation( |
| &mut self, |
| obligation: &PredicateObligation<'tcx>, |
| trait_obligation: PolyTraitObligation<'tcx>, |
| stalled_on: &mut Vec<TyOrConstInferVar>, |
| ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> { |
| let infcx = self.selcx.infcx; |
| if obligation.predicate.is_global() && !self.selcx.is_intercrate() { |
| // no type variables present, can use evaluation for better caching. |
| // FIXME: consider caching errors too. |
| if infcx.predicate_must_hold_considering_regions(obligation) { |
| debug!( |
| "selecting trait at depth {} evaluated to holds", |
| obligation.recursion_depth |
| ); |
| return ProcessResult::Changed(vec![]); |
| } |
| } |
| |
| match self.selcx.poly_select(&trait_obligation) { |
| Ok(Some(impl_source)) => { |
| debug!("selecting trait at depth {} yielded Ok(Some)", obligation.recursion_depth); |
| ProcessResult::Changed(mk_pending(impl_source.nested_obligations())) |
| } |
| Ok(None) => { |
| debug!("selecting trait at depth {} yielded Ok(None)", obligation.recursion_depth); |
| |
| // This is a bit subtle: for the most part, the |
| // only reason we can fail to make progress on |
| // trait selection is because we don't have enough |
| // information about the types in the trait. |
| stalled_on.clear(); |
| stalled_on.extend(args_infer_vars( |
| &self.selcx, |
| trait_obligation.predicate.map_bound(|pred| pred.trait_ref.args), |
| )); |
| |
| debug!( |
| "process_predicate: pending obligation {:?} now stalled on {:?}", |
| infcx.resolve_vars_if_possible(obligation.clone()), |
| stalled_on |
| ); |
| |
| ProcessResult::Unchanged |
| } |
| Err(selection_err) => { |
| debug!("selecting trait at depth {} yielded Err", obligation.recursion_depth); |
| |
| ProcessResult::Error(FulfillmentErrorCode::Select(selection_err)) |
| } |
| } |
| } |
| |
| fn process_projection_obligation( |
| &mut self, |
| obligation: &PredicateObligation<'tcx>, |
| project_obligation: PolyProjectionObligation<'tcx>, |
| stalled_on: &mut Vec<TyOrConstInferVar>, |
| ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> { |
| let tcx = self.selcx.tcx(); |
| |
| if obligation.predicate.is_global() && !self.selcx.is_intercrate() { |
| // no type variables present, can use evaluation for better caching. |
| // FIXME: consider caching errors too. |
| if self.selcx.infcx.predicate_must_hold_considering_regions(obligation) { |
| if let Some(key) = ProjectionCacheKey::from_poly_projection_obligation( |
| &mut self.selcx, |
| &project_obligation, |
| ) { |
| // If `predicate_must_hold_considering_regions` succeeds, then we've |
| // evaluated all sub-obligations. We can therefore mark the 'root' |
| // obligation as complete, and skip evaluating sub-obligations. |
| self.selcx |
| .infcx |
| .inner |
| .borrow_mut() |
| .projection_cache() |
| .complete(key, EvaluationResult::EvaluatedToOk); |
| } |
| return ProcessResult::Changed(vec![]); |
| } else { |
| debug!("Does NOT hold: {:?}", obligation); |
| } |
| } |
| |
| match project::poly_project_and_unify_term(&mut self.selcx, &project_obligation) { |
| ProjectAndUnifyResult::Holds(os) => ProcessResult::Changed(mk_pending(os)), |
| ProjectAndUnifyResult::FailedNormalization => { |
| stalled_on.clear(); |
| stalled_on.extend(args_infer_vars( |
| &self.selcx, |
| project_obligation.predicate.map_bound(|pred| pred.projection_term.args), |
| )); |
| ProcessResult::Unchanged |
| } |
| // Let the caller handle the recursion |
| ProjectAndUnifyResult::Recursive => ProcessResult::Changed(mk_pending(vec![ |
| project_obligation.with(tcx, project_obligation.predicate), |
| ])), |
| ProjectAndUnifyResult::MismatchedProjectionTypes(e) => { |
| ProcessResult::Error(FulfillmentErrorCode::Project(e)) |
| } |
| } |
| } |
| } |
| |
| /// Returns the set of inference variables contained in `args`. |
| fn args_infer_vars<'a, 'tcx>( |
| selcx: &SelectionContext<'a, 'tcx>, |
| args: ty::Binder<'tcx, GenericArgsRef<'tcx>>, |
| ) -> impl Iterator<Item = TyOrConstInferVar> + Captures<'tcx> { |
| selcx |
| .infcx |
| .resolve_vars_if_possible(args) |
| .skip_binder() // ok because this check doesn't care about regions |
| .iter() |
| .filter(|arg| arg.has_non_region_infer()) |
| .flat_map(|arg| { |
| let mut walker = arg.walk(); |
| while let Some(c) = walker.next() { |
| if !c.has_non_region_infer() { |
| walker.visited.remove(&c); |
| walker.skip_current_subtree(); |
| } |
| } |
| walker.visited.into_iter() |
| }) |
| .filter_map(TyOrConstInferVar::maybe_from_generic_arg) |
| } |
| |
| #[derive(Debug)] |
| pub struct OldSolverError<'tcx>( |
| Error<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>>, |
| ); |
| |
| impl<'tcx> FromSolverError<'tcx, OldSolverError<'tcx>> for FulfillmentError<'tcx> { |
| fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: OldSolverError<'tcx>) -> Self { |
| let mut iter = error.0.backtrace.into_iter(); |
| let obligation = iter.next().unwrap().obligation; |
| // The root obligation is the last item in the backtrace - if there's only |
| // one item, then it's the same as the main obligation |
| let root_obligation = iter.next_back().map_or_else(|| obligation.clone(), |e| e.obligation); |
| FulfillmentError::new(obligation, error.0.error, root_obligation) |
| } |
| } |
| |
| impl<'tcx> FromSolverError<'tcx, OldSolverError<'tcx>> for ScrubbedTraitError<'tcx> { |
| fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: OldSolverError<'tcx>) -> Self { |
| match error.0.error { |
| FulfillmentErrorCode::Select(_) |
| | FulfillmentErrorCode::Project(_) |
| | FulfillmentErrorCode::Subtype(_, _) |
| | FulfillmentErrorCode::ConstEquate(_, _) => ScrubbedTraitError::TrueError, |
| FulfillmentErrorCode::Ambiguity { overflow: _ } => ScrubbedTraitError::Ambiguity, |
| FulfillmentErrorCode::Cycle(cycle) => ScrubbedTraitError::Cycle(cycle), |
| } |
| } |
| } |