about summary refs log tree commit diff
path: root/compiler/rustc_trait_selection/src
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2022-12-27 00:39:36 +0000
committerMichael Goulet <michael@errs.io>2023-01-11 20:12:57 +0000
commitc8334ce60cce9571fcfdea31a3e67e3933fb7665 (patch)
tree1f63730aeb8a6734ac3aaec57a44238e003a246b /compiler/rustc_trait_selection/src
parentef4046e4f3932991971cdb64915172899532aece (diff)
downloadrust-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.rs220
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs13
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs45
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.