1use std::mem;
7
8use chalk_ir::cast::Cast;
9use hir_def::lang_item::LangItem;
10use hir_expand::name::Name;
11use intern::sym;
12use triomphe::Arc;
13
14use crate::{
15 Canonical, Goal, Interner, ProjectionTyExt, TraitEnvironment, Ty, TyBuilder, TyKind,
16 db::HirDatabase, infer::unify::InferenceTable,
17};
18
19const AUTODEREF_RECURSION_LIMIT: usize = 20;
20
21#[derive(Debug)]
22pub(crate) enum AutoderefKind {
23 Builtin,
24 Overloaded,
25}
26
27pub fn autoderef(
35 db: &dyn HirDatabase,
36 env: Arc<TraitEnvironment>,
37 ty: Canonical<Ty>,
38) -> impl Iterator<Item = Ty> {
39 let mut table = InferenceTable::new(db, env);
40 let ty = table.instantiate_canonical(ty);
41 let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false, false);
42 let mut v = Vec::new();
43 while let Some((ty, _steps)) = autoderef.next() {
44 let resolved = autoderef.table.resolve_completely(ty);
47
48 if v.contains(&resolved) {
55 break;
56 }
57 v.push(resolved);
58 }
59 v.into_iter()
60}
61
62trait TrackAutoderefSteps {
63 fn len(&self) -> usize;
64 fn push(&mut self, kind: AutoderefKind, ty: &Ty);
65}
66
67impl TrackAutoderefSteps for usize {
68 fn len(&self) -> usize {
69 *self
70 }
71 fn push(&mut self, _: AutoderefKind, _: &Ty) {
72 *self += 1;
73 }
74}
75impl TrackAutoderefSteps for Vec<(AutoderefKind, Ty)> {
76 fn len(&self) -> usize {
77 self.len()
78 }
79 fn push(&mut self, kind: AutoderefKind, ty: &Ty) {
80 self.push((kind, ty.clone()));
81 }
82}
83
84#[derive(Debug)]
85pub(crate) struct Autoderef<'table, 'db, T = Vec<(AutoderefKind, Ty)>> {
86 pub(crate) table: &'table mut InferenceTable<'db>,
87 ty: Ty,
88 at_start: bool,
89 steps: T,
90 explicit: bool,
91 use_receiver_trait: bool,
92}
93
94impl<'table, 'db> Autoderef<'table, 'db> {
95 pub(crate) fn new(
96 table: &'table mut InferenceTable<'db>,
97 ty: Ty,
98 explicit: bool,
99 use_receiver_trait: bool,
100 ) -> Self {
101 let ty = table.resolve_ty_shallow(&ty);
102 Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit, use_receiver_trait }
103 }
104
105 pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] {
106 &self.steps
107 }
108}
109
110impl<'table, 'db> Autoderef<'table, 'db, usize> {
111 pub(crate) fn new_no_tracking(
112 table: &'table mut InferenceTable<'db>,
113 ty: Ty,
114 explicit: bool,
115 use_receiver_trait: bool,
116 ) -> Self {
117 let ty = table.resolve_ty_shallow(&ty);
118 Autoderef { table, ty, at_start: true, steps: 0, explicit, use_receiver_trait }
119 }
120}
121
122#[allow(private_bounds)]
123impl<T: TrackAutoderefSteps> Autoderef<'_, '_, T> {
124 pub(crate) fn step_count(&self) -> usize {
125 self.steps.len()
126 }
127
128 pub(crate) fn final_ty(&self) -> Ty {
129 self.ty.clone()
130 }
131}
132
133impl<T: TrackAutoderefSteps> Iterator for Autoderef<'_, '_, T> {
134 type Item = (Ty, usize);
135
136 #[tracing::instrument(skip_all)]
137 fn next(&mut self) -> Option<Self::Item> {
138 if mem::take(&mut self.at_start) {
139 return Some((self.ty.clone(), 0));
140 }
141
142 if self.steps.len() > AUTODEREF_RECURSION_LIMIT {
143 return None;
144 }
145
146 let (kind, new_ty) =
147 autoderef_step(self.table, self.ty.clone(), self.explicit, self.use_receiver_trait)?;
148
149 self.steps.push(kind, &self.ty);
150 self.ty = new_ty;
151
152 Some((self.ty.clone(), self.step_count()))
153 }
154}
155
156pub(crate) fn autoderef_step(
157 table: &mut InferenceTable<'_>,
158 ty: Ty,
159 explicit: bool,
160 use_receiver_trait: bool,
161) -> Option<(AutoderefKind, Ty)> {
162 if let Some(derefed) = builtin_deref(table.db, &ty, explicit) {
163 Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
164 } else {
165 Some((AutoderefKind::Overloaded, deref_by_trait(table, ty, use_receiver_trait)?))
166 }
167}
168
169pub(crate) fn builtin_deref<'ty>(
170 db: &dyn HirDatabase,
171 ty: &'ty Ty,
172 explicit: bool,
173) -> Option<&'ty Ty> {
174 match ty.kind(Interner) {
175 TyKind::Ref(.., ty) => Some(ty),
176 TyKind::Raw(.., ty) if explicit => Some(ty),
177 &TyKind::Adt(chalk_ir::AdtId(adt), ref substs) if crate::lang_items::is_box(db, adt) => {
178 substs.at(Interner, 0).ty(Interner)
179 }
180 _ => None,
181 }
182}
183
184pub(crate) fn deref_by_trait(
185 table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>,
186 ty: Ty,
187 use_receiver_trait: bool,
188) -> Option<Ty> {
189 let _p = tracing::info_span!("deref_by_trait").entered();
190 if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() {
191 return None;
193 }
194
195 let trait_id = || {
196 #[expect(clippy::overly_complex_bool_expr)]
200 if use_receiver_trait
201 && false
202 && let Some(receiver) = LangItem::Receiver.resolve_trait(db, table.trait_env.krate)
203 {
204 return Some(receiver);
205 }
206 LangItem::Deref.resolve_trait(db, table.trait_env.krate)
209 };
210 let trait_id = trait_id()?;
211 let target =
212 trait_id.trait_items(db).associated_type_by_name(&Name::new_symbol_root(sym::Target))?;
213
214 let projection = {
215 let b = TyBuilder::subst_for_def(db, trait_id, None);
216 if b.remaining() != 1 {
217 return None;
220 }
221 let deref_subst = b.push(ty).build();
222 TyBuilder::assoc_type_projection(db, target, Some(deref_subst)).build()
223 };
224
225 let trait_ref = projection.trait_ref(db);
227 let implements_goal: Goal = trait_ref.cast(Interner);
228 if table.try_obligation(implements_goal.clone()).no_solution() {
229 return None;
230 }
231
232 table.register_obligation(implements_goal);
233
234 let result = table.normalize_projection_ty(projection);
235 Some(table.resolve_ty_shallow(&result))
236}