about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDonough Liu <ldm2993593805@163.com>2020-06-20 18:29:13 +0800
committerDonough Liu <ldm2993593805@163.com>2020-06-20 18:29:13 +0800
commit51555186b680ffc63b1daf362456f7f8ca537763 (patch)
treea4886661cd7c6f3453cf28ae8a5b6fff861d57be
parent34c5cd9a64d8537236626c4ccbed39a924cd38e2 (diff)
downloadrust-51555186b680ffc63b1daf362456f7f8ca537763.tar.gz
rust-51555186b680ffc63b1daf362456f7f8ca537763.zip
Decouple `Autoderef` with `FnCtxt` and move `Autoderef` to `librustc_trait_selection`.
-rw-r--r--src/librustc_trait_selection/autoderef.rs229
-rw-r--r--src/librustc_trait_selection/lib.rs1
-rw-r--r--src/librustc_typeck/check/autoderef.rs246
-rw-r--r--src/librustc_typeck/check/callee.rs13
-rw-r--r--src/librustc_typeck/check/coercion.rs2
-rw-r--r--src/librustc_typeck/check/expr.rs10
-rw-r--r--src/librustc_typeck/check/method/confirm.rs7
-rw-r--r--src/librustc_typeck/check/method/probe.rs4
-rw-r--r--src/librustc_typeck/check/place_op.rs9
-rw-r--r--src/librustc_typeck/check/wfcheck.rs2
10 files changed, 274 insertions, 249 deletions
diff --git a/src/librustc_trait_selection/autoderef.rs b/src/librustc_trait_selection/autoderef.rs
new file mode 100644
index 00000000000..d542e16d83f
--- /dev/null
+++ b/src/librustc_trait_selection/autoderef.rs
@@ -0,0 +1,229 @@
+use crate::traits::query::evaluate_obligation::InferCtxtExt;
+use crate::traits::{self, TraitEngine};
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_infer::infer::InferCtxt;
+use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, WithConstness};
+use rustc_middle::ty::{ToPredicate, TypeFoldable};
+use rustc_session::DiagnosticMessageId;
+use rustc_span::symbol::Ident;
+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<'a, '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.sess.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<'a, '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 = TraitRef {
+            def_id: tcx.lang_items().deref_trait()?,
+            substs: tcx.mk_substs_trait(ty, &[]),
+        };
+
+        let cause = traits::ObligationCause::misc(self.span, self.body_id);
+
+        let obligation = traits::Obligation::new(
+            cause.clone(),
+            self.param_env,
+            trait_ref.without_const().to_predicate(tcx),
+        );
+        if !self.infcx.predicate_may_hold(&obligation) {
+            debug!("overloaded_deref_ty: cannot match obligation");
+            return None;
+        }
+
+        let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot();
+        let normalized_ty = fulfillcx.normalize_projection_type(
+            &self.infcx,
+            self.param_env,
+            ty::ProjectionTy::from_ref_and_name(tcx, trait_ref, Ident::from_str("Target")),
+            cause,
+        );
+        if let Err(e) = fulfillcx.select_where_possible(&self.infcx) {
+            // 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", e);
+            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.clone()
+    }
+
+    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 = tcx.sess.recursion_limit() * 2;
+    let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`", ty);
+    let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg);
+    let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id);
+    if fresh {
+        struct_span_err!(
+            tcx.sess,
+            span,
+            E0055,
+            "reached the recursion limit while auto-dereferencing `{:?}`",
+            ty
+        )
+        .span_label(span, "deref recursion limit reached")
+        .help(&format!(
+            "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)",
+            suggested_limit, tcx.crate_name,
+        ))
+        .emit();
+    }
+}
diff --git a/src/librustc_trait_selection/lib.rs b/src/librustc_trait_selection/lib.rs
index ea886cd1f9e..4692fa04ed5 100644
--- a/src/librustc_trait_selection/lib.rs
+++ b/src/librustc_trait_selection/lib.rs
@@ -28,6 +28,7 @@ extern crate log;
 #[macro_use]
 extern crate rustc_middle;
 
+pub mod autoderef;
 pub mod infer;
 pub mod opaque_types;
 pub mod traits;
diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs
index 2570025959c..97d2b3e5a8e 100644
--- a/src/librustc_typeck/check/autoderef.rs
+++ b/src/librustc_typeck/check/autoderef.rs
@@ -1,191 +1,46 @@
+//! Some helper functions for `AutoDeref`
 use super::method::MethodCallee;
 use super::{FnCtxt, PlaceOp};
 
-use rustc_errors::struct_span_err;
-use rustc_hir as hir;
-use rustc_infer::infer::{InferCtxt, InferOk};
+use rustc_infer::infer::InferOk;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
-use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, WithConstness};
-use rustc_middle::ty::{ToPredicate, TypeFoldable};
-use rustc_session::DiagnosticMessageId;
-use rustc_span::symbol::Ident;
+use rustc_middle::ty::{self, Ty};
 use rustc_span::Span;
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
-use rustc_trait_selection::traits::{self, TraitEngine};
+use rustc_trait_selection::autoderef::{Autoderef, AutoderefKind};
 
 use std::iter;
 
-#[derive(Copy, Clone, Debug)]
-enum AutoderefKind {
-    Builtin,
-    Overloaded,
-}
-
-pub struct Autoderef<'a, 'tcx> {
-    infcx: &'a InferCtxt<'a, 'tcx>,
-    body_id: hir::HirId,
-    param_env: ty::ParamEnv<'tcx>,
-    steps: Vec<(Ty<'tcx>, AutoderefKind)>,
-    cur_ty: Ty<'tcx>,
-    obligations: Vec<traits::PredicateObligation<'tcx>>,
-    at_start: bool,
-    include_raw_pointers: bool,
-    span: Span,
-    silence_errors: bool,
-    reached_recursion_limit: 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.steps, self.cur_ty);
-        if self.at_start {
-            self.at_start = false;
-            debug!("autoderef stage #0 is {:?}", self.cur_ty);
-            return Some((self.cur_ty, 0));
-        }
-
-        if !tcx.sess.recursion_limit().value_within_limit(self.steps.len()) {
-            if !self.silence_errors {
-                report_autoderef_recursion_limit_error(tcx, self.span, self.cur_ty);
-            }
-            self.reached_recursion_limit = true;
-            return None;
-        }
-
-        if self.cur_ty.is_ty_var() {
-            return None;
-        }
-
-        // Otherwise, deref if type is derefable:
-        let (kind, new_ty) = if let Some(mt) = self.cur_ty.builtin_deref(self.include_raw_pointers)
-        {
-            (AutoderefKind::Builtin, mt.ty)
-        } else {
-            let ty = self.overloaded_deref_ty(self.cur_ty)?;
-            (AutoderefKind::Overloaded, ty)
-        };
-
-        if new_ty.references_error() {
-            return None;
-        }
-
-        self.steps.push((self.cur_ty, kind));
-        debug!(
-            "autoderef stage #{:?} is {:?} from {:?}",
-            self.steps.len(),
-            new_ty,
-            (self.cur_ty, kind)
-        );
-        self.cur_ty = new_ty;
-
-        Some((self.cur_ty, self.steps.len()))
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+    pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> {
+        Autoderef::new(self, self.param_env, self.body_id, span, base_ty)
     }
-}
 
-impl<'a, 'tcx> Autoderef<'a, 'tcx> {
-    pub fn new(
-        infcx: &'a InferCtxt<'a, 'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        body_id: hir::HirId,
+    pub fn try_overloaded_deref(
+        &self,
         span: Span,
         base_ty: Ty<'tcx>,
-    ) -> Autoderef<'a, 'tcx> {
-        Autoderef {
-            infcx,
-            body_id,
-            param_env,
-            steps: vec![],
-            cur_ty: infcx.resolve_vars_if_possible(&base_ty),
-            obligations: vec![],
-            at_start: true,
-            include_raw_pointers: false,
-            silence_errors: false,
-            reached_recursion_limit: false,
-            span,
-        }
-    }
-
-    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 = TraitRef {
-            def_id: tcx.lang_items().deref_trait()?,
-            substs: tcx.mk_substs_trait(ty, &[]),
-        };
-
-        let cause = traits::ObligationCause::misc(self.span, self.body_id);
-
-        let obligation = traits::Obligation::new(
-            cause.clone(),
-            self.param_env,
-            trait_ref.without_const().to_predicate(tcx),
-        );
-        if !self.infcx.predicate_may_hold(&obligation) {
-            debug!("overloaded_deref_ty: cannot match obligation");
-            return None;
-        }
-
-        let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot();
-        let normalized_ty = fulfillcx.normalize_projection_type(
-            &self.infcx,
-            self.param_env,
-            ty::ProjectionTy::from_ref_and_name(tcx, trait_ref, Ident::from_str("Target")),
-            cause,
-        );
-        if let Err(e) = fulfillcx.select_where_possible(&self.infcx) {
-            // 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", e);
-            return None;
-        }
-        let obligations = fulfillcx.pending_obligations();
-        debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
-        self.obligations.extend(obligations);
-
-        Some(self.infcx.resolve_vars_if_possible(&normalized_ty))
-    }
-
-    /// Returns the final type, generating an error if it is an
-    /// unresolved inference variable.
-    pub fn unambiguous_final_ty(&self, fcx: &FnCtxt<'a, 'tcx>) -> Ty<'tcx> {
-        fcx.structurally_resolved_type(self.span, self.cur_ty)
-    }
-
-    /// Returns the final type we ended up with, which may well be an
-    /// inference variable (we will resolve it first, if possible).
-    pub fn maybe_ambiguous_final_ty(&self) -> Ty<'tcx> {
-        self.infcx.resolve_vars_if_possible(&self.cur_ty)
-    }
-
-    pub fn step_count(&self) -> usize {
-        self.steps.len()
+    ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
+        self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref)
     }
 
     /// Returns the adjustment steps.
-    pub fn adjust_steps(&self, fcx: &FnCtxt<'a, 'tcx>) -> Vec<Adjustment<'tcx>> {
-        fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(fcx))
+    pub fn adjust_steps(&self, autoderef: &Autoderef<'a, 'tcx>) -> Vec<Adjustment<'tcx>> {
+        self.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(autoderef))
     }
 
     pub fn adjust_steps_as_infer_ok(
         &self,
-        fcx: &FnCtxt<'a, 'tcx>,
+        autoderef: &Autoderef<'a, 'tcx>,
     ) -> InferOk<'tcx, Vec<Adjustment<'tcx>>> {
         let mut obligations = vec![];
-        let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(self.cur_ty));
-        let steps: Vec<_> = self
-            .steps
+        let steps = autoderef.steps();
+        let targets =
+            steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty(false)));
+        let steps: Vec<_> = steps
             .iter()
             .map(|&(source, kind)| {
                 if let AutoderefKind::Overloaded = kind {
-                    fcx.try_overloaded_deref(self.span, source).and_then(
+                    self.try_overloaded_deref(autoderef.span(), source).and_then(
                         |InferOk { value: method, obligations: o }| {
                             obligations.extend(o);
                             if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
@@ -205,67 +60,4 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
 
         InferOk { obligations, value: steps }
     }
-
-    /// 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 reached_recursion_limit(&self) -> bool {
-        self.reached_recursion_limit
-    }
-
-    pub fn finalize(self, fcx: &FnCtxt<'a, 'tcx>) {
-        fcx.register_predicates(self.into_obligations());
-    }
-
-    pub fn into_obligations(self) -> Vec<traits::PredicateObligation<'tcx>> {
-        self.obligations
-    }
-}
-
-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 = tcx.sess.recursion_limit() * 2;
-    let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`", ty);
-    let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg);
-    let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id);
-    if fresh {
-        struct_span_err!(
-            tcx.sess,
-            span,
-            E0055,
-            "reached the recursion limit while auto-dereferencing `{:?}`",
-            ty
-        )
-        .span_label(span, "deref recursion limit reached")
-        .help(&format!(
-            "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)",
-            suggested_limit, tcx.crate_name,
-        ))
-        .emit();
-    }
-}
-
-impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
-    pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> {
-        Autoderef::new(self, self.param_env, self.body_id, span, base_ty)
-    }
-
-    pub fn try_overloaded_deref(
-        &self,
-        span: Span,
-        base_ty: Ty<'tcx>,
-    ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
-        self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref)
-    }
 }
diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index fe200a0ad2a..308ed5d8402 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -1,4 +1,3 @@
-use super::autoderef::Autoderef;
 use super::method::MethodCallee;
 use super::{Expectation, FnCtxt, TupleArgumentsFlag};
 use crate::type_error_struct;
@@ -17,6 +16,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
 use rustc_target::spec::abi;
+use rustc_trait_selection::autoderef::Autoderef;
 
 /// Checks that it is legal to call methods of the trait corresponding
 /// to `trait_id` (this only cares about the trait, not the specific
@@ -72,7 +72,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         while result.is_none() && autoderef.next().is_some() {
             result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef);
         }
-        autoderef.finalize(self);
+        self.register_predicates(autoderef.into_obligations());
 
         let output = match result {
             None => {
@@ -106,7 +106,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         arg_exprs: &'tcx [hir::Expr<'tcx>],
         autoderef: &Autoderef<'a, 'tcx>,
     ) -> Option<CallStep<'tcx>> {
-        let adjusted_ty = autoderef.unambiguous_final_ty(self);
+        let adjusted_ty =
+            self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
         debug!(
             "try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})",
             call_expr, adjusted_ty
@@ -115,7 +116,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // If the callee is a bare function or a closure, then we're all set.
         match adjusted_ty.kind {
             ty::FnDef(..) | ty::FnPtr(_) => {
-                let adjustments = autoderef.adjust_steps(self);
+                let adjustments = self.adjust_steps(autoderef);
                 self.apply_adjustments(callee_expr, adjustments);
                 return Some(CallStep::Builtin(adjusted_ty));
             }
@@ -135,7 +136,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             &closure_sig,
                         )
                         .0;
-                    let adjustments = autoderef.adjust_steps(self);
+                    let adjustments = self.adjust_steps(autoderef);
                     self.record_deferred_call_resolution(
                         def_id,
                         DeferredCallResolution {
@@ -176,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs))
             .or_else(|| self.try_overloaded_call_traits(call_expr, adjusted_ty, None))
             .map(|(autoref, method)| {
-                let mut adjustments = autoderef.adjust_steps(self);
+                let mut adjustments = self.adjust_steps(autoderef);
                 adjustments.extend(autoref);
                 self.apply_adjustments(callee_expr, adjustments);
                 CallStep::Overloaded(method)
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index 1c34d71ee31..7a2567d8dd2 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -422,7 +422,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         }
 
         let InferOk { value: mut adjustments, obligations: o } =
-            autoderef.adjust_steps_as_infer_ok(self);
+            self.adjust_steps_as_infer_ok(&autoderef);
         obligations.extend(o);
         obligations.extend(autoderef.into_obligations());
 
diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index 6a006676371..1eaa5a6c31e 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -1447,9 +1447,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // of error recovery.
                         self.write_field_index(expr.hir_id, index);
                         if field.vis.is_accessible_from(def_scope, self.tcx) {
-                            let adjustments = autoderef.adjust_steps(self);
+                            let adjustments = self.adjust_steps(&autoderef);
                             self.apply_adjustments(base, adjustments);
-                            autoderef.finalize(self);
+                            self.register_predicates(autoderef.into_obligations());
 
                             self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span);
                             return field_ty;
@@ -1462,9 +1462,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     if let Ok(index) = fstr.parse::<usize>() {
                         if fstr == index.to_string() {
                             if let Some(field_ty) = tys.get(index) {
-                                let adjustments = autoderef.adjust_steps(self);
+                                let adjustments = self.adjust_steps(&autoderef);
                                 self.apply_adjustments(base, adjustments);
-                                autoderef.finalize(self);
+                                self.register_predicates(autoderef.into_obligations());
 
                                 self.write_field_index(expr.hir_id, index);
                                 return field_ty.expect_ty();
@@ -1475,7 +1475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 _ => {}
             }
         }
-        autoderef.unambiguous_final_ty(self);
+        self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
 
         if let Some((did, field_ty)) = private_candidate {
             self.ban_private_field_access(expr, expr_t, field, did);
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index 6844c9416af..1c3d23a3a24 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -144,9 +144,10 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         };
         assert_eq!(n, pick.autoderefs);
 
-        let mut adjustments = autoderef.adjust_steps(self);
+        let mut adjustments = self.adjust_steps(&autoderef);
 
-        let mut target = autoderef.unambiguous_final_ty(self);
+        let mut target =
+            self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
 
         if let Some(mutbl) = pick.autoref {
             let region = self.next_region_var(infer::Autoref(self.span));
@@ -176,7 +177,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
             assert!(pick.unsize.is_none());
         }
 
-        autoderef.finalize(self);
+        self.register_predicates(autoderef.into_obligations());
 
         // Write out the final adjustments.
         self.apply_adjustments(self.self_expr, adjustments);
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index eb8f7668717..8c4ef24b945 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -3,7 +3,6 @@ use super::MethodError;
 use super::NoMatchData;
 use super::{CandidateSource, ImplSource, TraitSource};
 
-use crate::check::autoderef::{self, Autoderef};
 use crate::check::FnCtxt;
 use crate::hir::def::DefKind;
 use crate::hir::def_id::DefId;
@@ -30,6 +29,7 @@ use rustc_session::config::nightly_options;
 use rustc_session::lint;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP};
+use rustc_trait_selection::autoderef::{self, Autoderef};
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy;
 use rustc_trait_selection::traits::query::method_autoderef::{
@@ -477,7 +477,7 @@ fn method_autoderef_steps<'tcx>(
             })
             .collect();
 
-        let final_ty = autoderef.maybe_ambiguous_final_ty();
+        let final_ty = autoderef.final_ty(true);
         let opt_bad_ty = match final_ty.kind {
             ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy {
                 reached_raw_pointer,
diff --git a/src/librustc_typeck/check/place_op.rs b/src/librustc_typeck/check/place_op.rs
index d1c22cd1ac0..b7c8f310a14 100644
--- a/src/librustc_typeck/check/place_op.rs
+++ b/src/librustc_typeck/check/place_op.rs
@@ -1,4 +1,3 @@
-use crate::check::autoderef::Autoderef;
 use crate::check::method::MethodCallee;
 use crate::check::{FnCtxt, PlaceOp};
 use rustc_hir as hir;
@@ -9,6 +8,7 @@ use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutabili
 use rustc_middle::ty::{self, Ty};
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::Span;
+use rustc_trait_selection::autoderef::Autoderef;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already.
@@ -57,7 +57,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         while result.is_none() && autoderef.next().is_some() {
             result = self.try_index_step(expr, base_expr, &autoderef, idx_ty);
         }
-        autoderef.finalize(self);
+        self.register_predicates(autoderef.into_obligations());
         result
     }
 
@@ -73,7 +73,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         autoderef: &Autoderef<'a, 'tcx>,
         index_ty: Ty<'tcx>,
     ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
-        let adjusted_ty = autoderef.unambiguous_final_ty(self);
+        let adjusted_ty =
+            self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
         debug!(
             "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
              index_ty={:?})",
@@ -105,7 +106,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 debug!("try_index_step: success, using overloaded indexing");
                 let method = self.register_infer_ok_obligations(ok);
 
-                let mut adjustments = autoderef.adjust_steps(self);
+                let mut adjustments = self.adjust_steps(autoderef);
                 if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
                     adjustments.push(Adjustment {
                         kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index f3297ed6743..d1a86a7ee89 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -1118,7 +1118,7 @@ fn receiver_is_valid<'fcx, 'tcx>(
             );
 
             if can_eq_self(potential_self_ty) {
-                autoderef.finalize(fcx);
+                fcx.register_predicates(autoderef.into_obligations());
 
                 if let Some(mut err) =
                     fcx.demand_eqtype_with_origin(&cause, self_ty, potential_self_ty)