diff options
| author | Michael Goulet <michael@errs.io> | 2022-12-27 00:39:36 +0000 |
|---|---|---|
| committer | Michael Goulet <michael@errs.io> | 2023-01-11 20:12:57 +0000 |
| commit | c8334ce60cce9571fcfdea31a3e67e3933fb7665 (patch) | |
| tree | 1f63730aeb8a6734ac3aaec57a44238e003a246b /compiler/rustc_trait_selection/src | |
| parent | ef4046e4f3932991971cdb64915172899532aece (diff) | |
| download | rust-c8334ce60cce9571fcfdea31a3e67e3933fb7665.tar.gz rust-c8334ce60cce9571fcfdea31a3e67e3933fb7665.zip | |
Move autoderef to rustc_hir_analysis
Diffstat (limited to 'compiler/rustc_trait_selection/src')
| -rw-r--r-- | compiler/rustc_trait_selection/src/autoderef.rs | 220 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/errors.rs | 13 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/lib.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs | 45 |
4 files changed, 24 insertions, 255 deletions
diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_trait_selection/src/autoderef.rs deleted file mode 100644 index e988c77a064..00000000000 --- a/compiler/rustc_trait_selection/src/autoderef.rs +++ /dev/null @@ -1,220 +0,0 @@ -use crate::errors::AutoDerefReachedRecursionLimit; -use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::NormalizeExt; -use crate::traits::{self, TraitEngine, TraitEngineExt}; -use rustc_hir as hir; -use rustc_infer::infer::InferCtxt; -use rustc_middle::ty::TypeVisitable; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_session::Limit; -use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::Span; - -#[derive(Copy, Clone, Debug)] -pub enum AutoderefKind { - Builtin, - Overloaded, -} - -struct AutoderefSnapshot<'tcx> { - at_start: bool, - reached_recursion_limit: bool, - steps: Vec<(Ty<'tcx>, AutoderefKind)>, - cur_ty: Ty<'tcx>, - obligations: Vec<traits::PredicateObligation<'tcx>>, -} - -pub struct Autoderef<'a, 'tcx> { - // Meta infos: - infcx: &'a InferCtxt<'tcx>, - span: Span, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - - // Current state: - state: AutoderefSnapshot<'tcx>, - - // Configurations: - include_raw_pointers: bool, - silence_errors: bool, -} - -impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { - type Item = (Ty<'tcx>, usize); - - fn next(&mut self) -> Option<Self::Item> { - let tcx = self.infcx.tcx; - - debug!("autoderef: steps={:?}, cur_ty={:?}", self.state.steps, self.state.cur_ty); - if self.state.at_start { - self.state.at_start = false; - debug!("autoderef stage #0 is {:?}", self.state.cur_ty); - return Some((self.state.cur_ty, 0)); - } - - // If we have reached the recursion limit, error gracefully. - if !tcx.recursion_limit().value_within_limit(self.state.steps.len()) { - if !self.silence_errors { - report_autoderef_recursion_limit_error(tcx, self.span, self.state.cur_ty); - } - self.state.reached_recursion_limit = true; - return None; - } - - if self.state.cur_ty.is_ty_var() { - return None; - } - - // Otherwise, deref if type is derefable: - let (kind, new_ty) = - if let Some(mt) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) { - (AutoderefKind::Builtin, mt.ty) - } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) { - (AutoderefKind::Overloaded, ty) - } else { - return None; - }; - - if new_ty.references_error() { - return None; - } - - self.state.steps.push((self.state.cur_ty, kind)); - debug!( - "autoderef stage #{:?} is {:?} from {:?}", - self.step_count(), - new_ty, - (self.state.cur_ty, kind) - ); - self.state.cur_ty = new_ty; - - Some((self.state.cur_ty, self.step_count())) - } -} - -impl<'a, 'tcx> Autoderef<'a, 'tcx> { - pub fn new( - infcx: &'a InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, - span: Span, - base_ty: Ty<'tcx>, - ) -> Autoderef<'a, 'tcx> { - Autoderef { - infcx, - span, - body_id, - param_env, - state: AutoderefSnapshot { - steps: vec![], - cur_ty: infcx.resolve_vars_if_possible(base_ty), - obligations: vec![], - at_start: true, - reached_recursion_limit: false, - }, - include_raw_pointers: false, - silence_errors: false, - } - } - - fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { - debug!("overloaded_deref_ty({:?})", ty); - - let tcx = self.infcx.tcx; - - // <ty as Deref> - let trait_ref = tcx.mk_trait_ref(tcx.lang_items().deref_trait()?, [ty]); - - let cause = traits::ObligationCause::misc(self.span, self.body_id); - - let obligation = traits::Obligation::new( - tcx, - cause.clone(), - self.param_env, - ty::Binder::dummy(trait_ref), - ); - if !self.infcx.predicate_may_hold(&obligation) { - debug!("overloaded_deref_ty: cannot match obligation"); - return None; - } - - let normalized_ty = self - .infcx - .at(&cause, self.param_env) - .normalize(tcx.mk_projection(tcx.lang_items().deref_target()?, trait_ref.substs)); - let mut fulfillcx = <dyn TraitEngine<'tcx>>::new_in_snapshot(tcx); - let normalized_ty = - normalized_ty.into_value_registering_obligations(self.infcx, &mut *fulfillcx); - let errors = fulfillcx.select_where_possible(&self.infcx); - if !errors.is_empty() { - // This shouldn't happen, except for evaluate/fulfill mismatches, - // but that's not a reason for an ICE (`predicate_may_hold` is conservative - // by design). - debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling", errors); - return None; - } - let obligations = fulfillcx.pending_obligations(); - debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); - self.state.obligations.extend(obligations); - - Some(self.infcx.resolve_vars_if_possible(normalized_ty)) - } - - /// Returns the final type we ended up with, which may be an inference - /// variable (we will resolve it first, if we want). - pub fn final_ty(&self, resolve: bool) -> Ty<'tcx> { - if resolve { - self.infcx.resolve_vars_if_possible(self.state.cur_ty) - } else { - self.state.cur_ty - } - } - - pub fn step_count(&self) -> usize { - self.state.steps.len() - } - - pub fn into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>> { - self.state.obligations - } - - pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] { - &self.state.steps - } - - pub fn span(&self) -> Span { - self.span - } - - pub fn reached_recursion_limit(&self) -> bool { - self.state.reached_recursion_limit - } - - /// also dereference through raw pointer types - /// e.g., assuming ptr_to_Foo is the type `*const Foo` - /// fcx.autoderef(span, ptr_to_Foo) => [*const Foo] - /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo] - pub fn include_raw_pointers(mut self) -> Self { - self.include_raw_pointers = true; - self - } - - pub fn silence_errors(mut self) -> Self { - self.silence_errors = true; - self - } -} - -pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { - // We've reached the recursion limit, error gracefully. - let suggested_limit = match tcx.recursion_limit() { - Limit(0) => Limit(2), - limit => limit * 2, - }; - tcx.sess.emit_err(AutoDerefReachedRecursionLimit { - span, - ty, - suggested_limit, - crate_name: tcx.crate_name(LOCAL_CRATE), - }); -} diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 19f404cb5b7..4405537c645 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,7 +1,6 @@ use rustc_errors::{fluent, ErrorGuaranteed, Handler, IntoDiagnostic}; use rustc_macros::Diagnostic; use rustc_middle::ty::{self, PolyTraitRef, Ty}; -use rustc_session::Limit; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] @@ -22,18 +21,6 @@ pub struct UnableToConstructConstantValue<'a> { } #[derive(Diagnostic)] -#[help] -#[diag(trait_selection_auto_deref_reached_recursion_limit, code = "E0055")] -pub struct AutoDerefReachedRecursionLimit<'a> { - #[primary_span] - #[label] - pub span: Span, - pub ty: Ty<'a>, - pub suggested_limit: Limit, - pub crate_name: Symbol, -} - -#[derive(Diagnostic)] #[diag(trait_selection_empty_on_clause_in_rustc_on_unimplemented, code = "E0232")] pub struct EmptyOnClauseInOnUnimplemented { #[primary_span] diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index a30d1df4ede..081ac966c69 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -35,7 +35,6 @@ extern crate rustc_middle; #[macro_use] extern crate smallvec; -pub mod autoderef; pub mod errors; pub mod infer; pub mod solve; diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 43985495827..6c6696439c1 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -5,7 +5,6 @@ use super::{ PredicateObligation, }; -use crate::autoderef::Autoderef; use crate::infer::InferCtxt; use crate::traits::{NormalizeExt, ObligationCtxt}; @@ -750,26 +749,30 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } if let ty::Ref(region, base_ty, mutbl) = *real_ty.skip_binder().kind() { - let mut autoderef = Autoderef::new( - self, - obligation.param_env, - obligation.cause.body_id, - span, - base_ty, - ); - if let Some(steps) = autoderef.find_map(|(ty, steps)| { - // Re-add the `&` - let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); - - // Remapping bound vars here - let real_trait_pred_and_ty = - real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty)); - let obligation = self.mk_trait_obligation_with_new_self_ty( - obligation.param_env, - real_trait_pred_and_ty, - ); - Some(steps).filter(|_| self.predicate_may_hold(&obligation)) - }) { + let autoderef = (self.autoderef_steps)(base_ty); + if let Some(steps) = + autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| { + // Re-add the `&` + let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); + + // Remapping bound vars here + let real_trait_pred_and_ty = + real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty)); + let obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + real_trait_pred_and_ty, + ); + if obligations + .iter() + .chain([&obligation]) + .all(|obligation| self.predicate_may_hold(obligation)) + { + Some(steps) + } else { + None + } + }) + { if steps > 0 { // Don't care about `&mut` because `DerefMut` is used less // often and user will not expect autoderef happens. |
