1 //! In certain situations, rust automatically inserts derefs as necessary: for
2 //! example, field accesses `foo.bar` still work when `foo` is actually a
3 //! reference to a type with the field `bar`. This is an approximation of the
4 //! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs).
8 use chalk_ir::cast::Cast;
9 use hir_expand::name::name;
14 db::HirDatabase, infer::unify::InferenceTable, Canonical, Goal, Interner, ProjectionTyExt,
15 TraitEnvironment, Ty, TyBuilder, TyKind,
18 static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10);
20 pub(crate) enum AutoderefKind {
25 pub(crate) struct Autoderef<'a, 'db> {
26 pub(crate) table: &'a mut InferenceTable<'db>,
29 steps: Vec<(AutoderefKind, Ty)>,
32 impl<'a, 'db> Autoderef<'a, 'db> {
33 pub(crate) fn new(table: &'a mut InferenceTable<'db>, ty: Ty) -> Self {
34 let ty = table.resolve_ty_shallow(&ty);
35 Autoderef { table, ty, at_start: true, steps: Vec::new() }
38 pub(crate) fn step_count(&self) -> usize {
42 pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] {
46 pub(crate) fn final_ty(&self) -> Ty {
51 impl Iterator for Autoderef<'_, '_> {
52 type Item = (Ty, usize);
54 fn next(&mut self) -> Option<Self::Item> {
56 self.at_start = false;
57 return Some((self.ty.clone(), 0));
60 if AUTODEREF_RECURSION_LIMIT.check(self.steps.len() + 1).is_err() {
64 let (kind, new_ty) = autoderef_step(self.table, self.ty.clone())?;
66 self.steps.push((kind, self.ty.clone()));
69 Some((self.ty.clone(), self.step_count()))
73 pub(crate) fn autoderef_step(
74 table: &mut InferenceTable<'_>,
76 ) -> Option<(AutoderefKind, Ty)> {
77 if let Some(derefed) = builtin_deref(&ty) {
78 Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
80 Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?))
84 // FIXME: replace uses of this with Autoderef above
87 env: Arc<TraitEnvironment>,
89 ) -> impl Iterator<Item = Canonical<Ty>> + '_ {
90 let mut table = InferenceTable::new(db, env);
91 let ty = table.instantiate_canonical(ty);
92 let mut autoderef = Autoderef::new(&mut table, ty);
93 let mut v = Vec::new();
94 while let Some((ty, _steps)) = autoderef.next() {
95 v.push(autoderef.table.canonicalize(ty).value);
100 pub(crate) fn deref(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
101 let _p = profile::span("deref");
102 autoderef_step(table, ty).map(|(_, ty)| ty)
105 fn builtin_deref(ty: &Ty) -> Option<&Ty> {
106 match ty.kind(Interner) {
107 TyKind::Ref(.., ty) | TyKind::Raw(.., ty) => Some(ty),
112 fn deref_by_trait(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
113 let _p = profile::span("deref_by_trait");
114 if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() {
115 // don't try to deref unknown variables
121 .lang_item(table.trait_env.krate, SmolStr::new_inline("deref"))
122 .and_then(|l| l.as_trait())?;
123 let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
126 let b = TyBuilder::subst_for_def(db, deref_trait, None);
127 if b.remaining() != 1 {
128 // the Target type + Deref trait should only have one generic parameter,
129 // namely Deref's Self type
132 let deref_subst = b.push(ty).build();
133 TyBuilder::assoc_type_projection(db, target, Some(deref_subst)).build()
136 // Check that the type implements Deref at all
137 let trait_ref = projection.trait_ref(db);
138 let implements_goal: Goal = trait_ref.cast(Interner);
139 table.try_obligation(implements_goal.clone())?;
141 table.register_obligation(implements_goal);
143 let result = table.normalize_projection_ty(projection);
144 Some(table.resolve_ty_shallow(&result))