Skip to main content

hir_ty/infer/
mutability.rs

1//! Finds if an expression is an immutable context or a mutable context, which is used in selecting
2//! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar.
3
4use hir_def::hir::{
5    Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, RecordSpread,
6    Statement, UnaryOp,
7};
8use rustc_ast_ir::Mutability;
9
10use crate::{
11    Adjust, AutoBorrow, OverloadedDeref,
12    infer::{InferenceContext, place_op::PlaceOp},
13    lower::lower_mutability,
14};
15
16impl<'db> InferenceContext<'db> {
17    pub(crate) fn infer_mut_body(&mut self, body_expr: ExprId) {
18        self.infer_mut_expr(body_expr, Mutability::Not);
19    }
20
21    fn infer_mut_expr(&mut self, tgt_expr: ExprId, mut mutability: Mutability) {
22        if let Some(adjustments) = self.result.expr_adjustments.get_mut(&tgt_expr) {
23            let mut adjustments = adjustments.iter_mut().rev().peekable();
24            while let Some(adj) = adjustments.next() {
25                match &mut adj.kind {
26                    Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => (),
27                    Adjust::Deref(Some(d)) => {
28                        if mutability == Mutability::Mut {
29                            let source_ty = match adjustments.peek() {
30                                Some(prev_adj) => prev_adj.target.as_ref(),
31                                None => self.result.type_of_expr[tgt_expr].as_ref(),
32                            };
33                            if let Some(infer_ok) = Self::try_mutable_overloaded_place_op(
34                                &self.table,
35                                tgt_expr,
36                                source_ty,
37                                None,
38                                PlaceOp::Deref,
39                            ) {
40                                self.table.register_predicates(infer_ok.obligations);
41                            }
42                            *d = OverloadedDeref(mutability);
43                        }
44                    }
45                    Adjust::Borrow(b) => match b {
46                        AutoBorrow::Ref(m) => mutability = (*m).into(),
47                        AutoBorrow::RawPtr(m) => mutability = *m,
48                    },
49                }
50            }
51        }
52        self.infer_mut_expr_without_adjust(tgt_expr, mutability);
53    }
54
55    fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) {
56        match &self.store[tgt_expr] {
57            Expr::Missing => (),
58            Expr::InlineAsm(e) => {
59                e.operands.iter().for_each(|(_, op)| match op {
60                    AsmOperand::In { expr, .. }
61                    | AsmOperand::Out { expr: Some(expr), .. }
62                    | AsmOperand::InOut { expr, .. } => {
63                        self.infer_mut_expr_without_adjust(*expr, Mutability::Not)
64                    }
65                    AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
66                        self.infer_mut_expr_without_adjust(*in_expr, Mutability::Not);
67                        if let Some(out_expr) = out_expr {
68                            self.infer_mut_expr_without_adjust(*out_expr, Mutability::Not);
69                        }
70                    }
71                    AsmOperand::Out { expr: None, .. }
72                    | AsmOperand::Label(_)
73                    | AsmOperand::Sym(_)
74                    | AsmOperand::Const(_) => (),
75                });
76            }
77            Expr::OffsetOf(_) => (),
78            &Expr::If { condition, then_branch, else_branch } => {
79                self.infer_mut_expr(condition, Mutability::Not);
80                self.infer_mut_expr(then_branch, Mutability::Not);
81                if let Some(else_branch) = else_branch {
82                    self.infer_mut_expr(else_branch, Mutability::Not);
83                }
84            }
85            Expr::Const(id) => {
86                self.infer_mut_expr(*id, Mutability::Not);
87            }
88            Expr::Let { pat, expr } => self.infer_mut_expr(*expr, self.pat_bound_mutability(*pat)),
89            Expr::Block { id: _, statements, tail, label: _ }
90            | Expr::Unsafe { id: _, statements, tail } => {
91                for st in statements.iter() {
92                    match st {
93                        Statement::Let { pat, type_ref: _, initializer, else_branch } => {
94                            if let Some(i) = initializer {
95                                self.infer_mut_expr(*i, self.pat_bound_mutability(*pat));
96                            }
97                            if let Some(e) = else_branch {
98                                self.infer_mut_expr(*e, Mutability::Not);
99                            }
100                        }
101                        Statement::Expr { expr, has_semi: _ } => {
102                            self.infer_mut_expr(*expr, Mutability::Not);
103                        }
104                        Statement::Item(_) => (),
105                    }
106                }
107                if let Some(tail) = tail {
108                    self.infer_mut_expr(*tail, Mutability::Not);
109                }
110            }
111            Expr::MethodCall { receiver: it, method_name: _, args, generic_args: _ }
112            | Expr::Call { callee: it, args } => {
113                self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*it)));
114            }
115            Expr::Match { expr, arms } => {
116                let m = self.pat_iter_bound_mutability(arms.iter().map(|it| it.pat));
117                self.infer_mut_expr(*expr, m);
118                for arm in arms.iter() {
119                    self.infer_mut_expr(arm.expr, Mutability::Not);
120                    if let Some(g) = arm.guard {
121                        self.infer_mut_expr(g, Mutability::Not);
122                    }
123                }
124            }
125            Expr::Yield { expr }
126            | Expr::Yeet { expr }
127            | Expr::Return { expr }
128            | Expr::Break { expr, label: _ } => {
129                if let &Some(expr) = expr {
130                    self.infer_mut_expr(expr, Mutability::Not);
131                }
132            }
133            Expr::Become { expr } => {
134                self.infer_mut_expr(*expr, Mutability::Not);
135            }
136            Expr::RecordLit { path: _, fields, spread, .. } => {
137                self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr));
138                if let RecordSpread::Expr(expr) = *spread {
139                    self.infer_mut_expr(expr, Mutability::Not);
140                }
141            }
142            &Expr::Index { base, index } => {
143                if mutability == Mutability::Mut {
144                    self.convert_place_op_to_mutable(PlaceOp::Index, tgt_expr, base, Some(index));
145                }
146                self.infer_mut_expr(base, mutability);
147                self.infer_mut_expr(index, Mutability::Not);
148            }
149            Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
150                if mutability == Mutability::Mut {
151                    self.convert_place_op_to_mutable(PlaceOp::Deref, tgt_expr, *expr, None);
152                }
153                self.infer_mut_expr(*expr, mutability);
154            }
155            Expr::Field { expr, name: _ } => {
156                self.infer_mut_expr(*expr, mutability);
157            }
158            Expr::UnaryOp { expr, op: _ }
159            | Expr::Range { lhs: Some(expr), rhs: None, range_type: _ }
160            | Expr::Range { rhs: Some(expr), lhs: None, range_type: _ }
161            | Expr::Await { expr }
162            | Expr::Loop { body: expr, label: _, source: _ }
163            | Expr::Cast { expr, type_ref: _ } => {
164                self.infer_mut_expr(*expr, Mutability::Not);
165            }
166            Expr::Ref { expr, rawness: _, mutability } => {
167                let mutability = lower_mutability(*mutability);
168                self.infer_mut_expr(*expr, mutability);
169            }
170            Expr::BinaryOp { lhs, rhs, op: Some(BinaryOp::Assignment { .. }) } => {
171                self.infer_mut_expr(*lhs, Mutability::Mut);
172                self.infer_mut_expr(*rhs, Mutability::Not);
173            }
174            &Expr::Assignment { target, value } => {
175                self.store.walk_pats(target, &mut |pat| match self.store[pat] {
176                    Pat::Expr(expr) => self.infer_mut_expr(expr, Mutability::Mut),
177                    Pat::ConstBlock(block) => self.infer_mut_expr(block, Mutability::Not),
178                    _ => {}
179                });
180                self.infer_mut_expr(value, Mutability::Not);
181            }
182            Expr::Array(Array::Repeat { initializer: lhs, repeat: rhs })
183            | Expr::BinaryOp { lhs, rhs, op: _ }
184            | Expr::Range { lhs: Some(lhs), rhs: Some(rhs), range_type: _ } => {
185                self.infer_mut_expr(*lhs, Mutability::Not);
186                self.infer_mut_expr(*rhs, Mutability::Not);
187            }
188            Expr::Closure { body, .. } => {
189                self.infer_mut_expr(*body, Mutability::Not);
190            }
191            Expr::Tuple { exprs } | Expr::Array(Array::ElementList { elements: exprs }) => {
192                self.infer_mut_not_expr_iter(exprs.iter().copied());
193            }
194            // These don't need any action, as they don't have sub expressions
195            Expr::Range { lhs: None, rhs: None, range_type: _ }
196            | Expr::Literal(_)
197            | Expr::Path(_)
198            | Expr::Continue { .. }
199            | Expr::Underscore
200            | Expr::IncludeBytes => (),
201        }
202    }
203
204    fn infer_mut_not_expr_iter(&mut self, exprs: impl Iterator<Item = ExprId>) {
205        for expr in exprs {
206            self.infer_mut_expr(expr, Mutability::Not);
207        }
208    }
209
210    fn pat_iter_bound_mutability(&self, mut pat: impl Iterator<Item = PatId>) -> Mutability {
211        if pat.any(|p| self.pat_bound_mutability(p) == Mutability::Mut) {
212            Mutability::Mut
213        } else {
214            Mutability::Not
215        }
216    }
217
218    /// Checks if the pat contains a `ref mut` binding. Such paths makes the context of bounded expressions
219    /// mutable. For example in `let (ref mut x0, ref x1) = *it;` we need to use `DerefMut` for `*it` but in
220    /// `let (ref x0, ref x1) = *it;` we should use `Deref`.
221    fn pat_bound_mutability(&self, pat: PatId) -> Mutability {
222        let mut r = Mutability::Not;
223        self.store.walk_bindings_in_pat(pat, |b| {
224            if self.store[b].mode == BindingAnnotation::RefMut {
225                r = Mutability::Mut;
226            }
227        });
228        r
229    }
230}