1use 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 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 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}