diff options
Diffstat (limited to 'compiler/rustc_trait_selection')
4 files changed, 196 insertions, 8 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs new file mode 100644 index 00000000000..bd8c04b7655 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -0,0 +1,152 @@ +use rustc_hir as hir; +use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt}; +use rustc_infer::traits::{ImplSource, Obligation, PredicateObligation}; +use rustc_middle::ty::fast_reject::DeepRejectCtxt; +use rustc_middle::{span_bug, ty}; +use rustc_type_ir::solve::NoSolution; +use thin_vec::ThinVec; + +use super::SelectionContext; + +pub type HostEffectObligation<'tcx> = Obligation<'tcx, ty::HostEffectPredicate<'tcx>>; + +pub enum EvaluationFailure { + Ambiguous, + NoSolution, +} + +pub fn evaluate_host_effect_obligation<'tcx>( + selcx: &mut SelectionContext<'_, 'tcx>, + obligation: &HostEffectObligation<'tcx>, +) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { + if selcx.infcx.intercrate { + span_bug!( + obligation.cause.span, + "should not select host obligation in old solver in intercrate mode" + ); + } + + match evaluate_host_effect_from_bounds(selcx, obligation) { + Ok(result) => return Ok(result), + Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), + Err(EvaluationFailure::NoSolution) => {} + } + + match evaluate_host_effect_from_selection_candiate(selcx, obligation) { + Ok(result) => return Ok(result), + Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), + Err(EvaluationFailure::NoSolution) => {} + } + + Err(EvaluationFailure::NoSolution) +} + +fn match_candidate<'tcx>( + infcx: &InferCtxt<'tcx>, + obligation: &HostEffectObligation<'tcx>, + candidate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, +) -> Result<ThinVec<PredicateObligation<'tcx>>, NoSolution> { + if !candidate.skip_binder().host.satisfies(obligation.predicate.host) { + return Err(NoSolution); + } + + let candidate = infcx.instantiate_binder_with_fresh_vars( + obligation.cause.span, + BoundRegionConversionTime::HigherRankedType, + candidate, + ); + + let mut nested = infcx + .at(&obligation.cause, obligation.param_env) + .eq(DefineOpaqueTypes::Yes, obligation.predicate.trait_ref, candidate.trait_ref)? + .into_obligations(); + + for nested in &mut nested { + nested.set_depth_from_parent(obligation.recursion_depth); + } + + Ok(nested) +} + +fn evaluate_host_effect_from_bounds<'tcx>( + selcx: &mut SelectionContext<'_, 'tcx>, + obligation: &HostEffectObligation<'tcx>, +) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { + let infcx = selcx.infcx; + let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx()); + let mut candidate = None; + + for predicate in obligation.param_env.caller_bounds() { + let bound_predicate = predicate.kind(); + if let ty::ClauseKind::HostEffect(data) = predicate.kind().skip_binder() { + let data = bound_predicate.rebind(data); + if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id { + continue; + } + + if !drcx.args_may_unify( + obligation.predicate.trait_ref.args, + data.skip_binder().trait_ref.args, + ) { + continue; + } + + let is_match = infcx.probe(|_| match_candidate(infcx, obligation, data).is_ok()); + + if is_match { + if candidate.is_some() { + return Err(EvaluationFailure::Ambiguous); + } else { + candidate = Some(data); + } + } + } + } + + if let Some(data) = candidate { + Ok(match_candidate(infcx, obligation, data) + .expect("candidate matched before, so it should match again")) + } else { + Err(EvaluationFailure::NoSolution) + } +} + +fn evaluate_host_effect_from_selection_candiate<'tcx>( + selcx: &mut SelectionContext<'_, 'tcx>, + obligation: &HostEffectObligation<'tcx>, +) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { + let tcx = selcx.tcx(); + selcx.infcx.commit_if_ok(|_| { + match selcx.select(&obligation.with(tcx, obligation.predicate.trait_ref)) { + Ok(None) => Err(EvaluationFailure::Ambiguous), + Err(_) => Err(EvaluationFailure::NoSolution), + Ok(Some(source)) => match source { + ImplSource::UserDefined(impl_) => { + if tcx.constness(impl_.impl_def_id) != hir::Constness::Const { + return Err(EvaluationFailure::NoSolution); + } + + let mut nested = impl_.nested; + nested.extend( + tcx.const_conditions(impl_.impl_def_id) + .instantiate(tcx, impl_.args) + .into_iter() + .map(|(trait_ref, _)| { + obligation.with( + tcx, + trait_ref.to_host_effect_clause(tcx, obligation.predicate.host), + ) + }), + ); + + for nested in &mut nested { + nested.set_depth_from_parent(obligation.recursion_depth); + } + + Ok(nested) + } + _ => Err(EvaluationFailure::NoSolution), + }, + } + }) +} diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 1754418156d..e3ad21e352a 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -17,6 +17,7 @@ use rustc_middle::ty::{self, Binder, Const, GenericArgsRef, TypeVisitableExt}; use thin_vec::ThinVec; use tracing::{debug, debug_span, instrument}; +use super::effects::{self, HostEffectObligation}; use super::project::{self, ProjectAndUnifyResult}; use super::select::SelectionContext; use super::{ @@ -402,8 +403,13 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ) } - ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => { - ProcessResult::Changed(Default::default()) + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(data)) => { + let host_obligation = obligation.with(infcx.tcx, data); + + self.process_host_obligation( + host_obligation, + &mut pending_obligation.stalled_on, + ) } ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(data)) => { @@ -854,6 +860,27 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { } } } + + fn process_host_obligation( + &mut self, + host_obligation: HostEffectObligation<'tcx>, + stalled_on: &mut Vec<TyOrConstInferVar>, + ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> { + match effects::evaluate_host_effect_obligation(&mut self.selcx, &host_obligation) { + Ok(nested) => ProcessResult::Changed(mk_pending(nested)), + Err(effects::EvaluationFailure::Ambiguous) => { + stalled_on.clear(); + stalled_on.extend(args_infer_vars( + &self.selcx, + ty::Binder::dummy(host_obligation.predicate.trait_ref.args), + )); + ProcessResult::Unchanged + } + Err(effects::EvaluationFailure::NoSolution) => { + ProcessResult::Error(FulfillmentErrorCode::Select(SelectionError::Unimplemented)) + } + } + } } /// Returns the set of inference variables contained in `args`. diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index cdf24887e76..f5d9b50359c 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -6,6 +6,7 @@ pub mod auto_trait; pub(crate) mod coherence; pub mod const_evaluatable; mod dyn_compatibility; +pub mod effects; mod engine; mod fulfill; pub mod misc; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index ec4114fd9d7..635d3bc99b1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -49,7 +49,7 @@ use crate::infer::{InferCtxt, InferOk, TypeFreshener}; use crate::solve::InferCtxtSelectExt as _; use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt}; -use crate::traits::{ProjectionCacheKey, Unimplemented}; +use crate::traits::{ProjectionCacheKey, Unimplemented, effects}; mod _match; mod candidate_assembly; @@ -645,11 +645,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.evaluate_trait_predicate_recursively(previous_stack, obligation) } - ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => { - // FIXME(effects): It should be relatively straightforward to implement - // old trait solver support for `HostEffect` bounds; or at least basic - // support for them. - todo!() + ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(data)) => { + self.infcx.enter_forall(bound_predicate.rebind(data), |data| { + match effects::evaluate_host_effect_obligation( + self, + &obligation.with(self.tcx(), data), + ) { + Ok(nested) => { + self.evaluate_predicates_recursively(previous_stack, nested) + } + Err(effects::EvaluationFailure::Ambiguous) => Ok(EvaluatedToAmbig), + Err(effects::EvaluationFailure::NoSolution) => Ok(EvaluatedToErr), + } + }) } ty::PredicateKind::Subtype(p) => { |
