]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
New upstream version 1.58.1+dfsg1
[rustc.git] / compiler / rustc_const_eval / src / transform / check_consts / resolver.rs
CommitLineData
e74abb32
XL
1//! Propagate `Qualif`s between locals and query the results.
2//!
3//! This contains the dataflow analysis used to track `Qualif`s on complex control-flow graphs.
4
e74abb32 5use rustc_index::bit_set::BitSet;
ba9703b0 6use rustc_middle::mir::visit::Visitor;
3c0e092e
XL
7use rustc_middle::mir::{self, BasicBlock, Local, Location, Statement, StatementKind};
8use rustc_mir_dataflow::fmt::DebugWithContext;
9use rustc_mir_dataflow::JoinSemiLattice;
10use rustc_span::DUMMY_SP;
e74abb32 11
3c0e092e 12use std::fmt;
e74abb32
XL
13use std::marker::PhantomData;
14
f9f354fc 15use super::{qualifs, ConstCx, Qualif};
e74abb32
XL
16
17/// A `Visitor` that propagates qualifs between locals. This defines the transfer function of
18/// `FlowSensitiveAnalysis`.
19///
3c0e092e
XL
20/// To account for indirect assignments, data flow conservatively assumes that local becomes
21/// qualified immediately after it is borrowed or its address escapes. The borrow must allow for
22/// mutation, which includes shared borrows of places with interior mutability. The type of
23/// borrowed place must contain the qualif.
e74abb32 24struct TransferFunction<'a, 'mir, 'tcx, Q> {
f9f354fc 25 ccx: &'a ConstCx<'mir, 'tcx>,
3c0e092e 26 state: &'a mut State,
e74abb32
XL
27 _qualif: PhantomData<Q>,
28}
29
30impl<Q> TransferFunction<'a, 'mir, 'tcx, Q>
31where
32 Q: Qualif,
33{
3c0e092e
XL
34 fn new(ccx: &'a ConstCx<'mir, 'tcx>, state: &'a mut State) -> Self {
35 TransferFunction { ccx, state, _qualif: PhantomData }
e74abb32
XL
36 }
37
38 fn initialize_state(&mut self) {
3c0e092e
XL
39 self.state.qualif.clear();
40 self.state.borrow.clear();
e74abb32 41
f9f354fc
XL
42 for arg in self.ccx.body.args_iter() {
43 let arg_ty = self.ccx.body.local_decls[arg].ty;
44 if Q::in_any_value_of_ty(self.ccx, arg_ty) {
3c0e092e 45 self.state.qualif.insert(arg);
e74abb32
XL
46 }
47 }
48 }
49
3c0e092e 50 fn assign_qualif_direct(&mut self, place: &mir::Place<'tcx>, mut value: bool) {
e74abb32
XL
51 debug_assert!(!place.is_indirect());
52
3c0e092e
XL
53 if !value {
54 for (base, _elem) in place.iter_projections() {
55 let base_ty = base.ty(self.ccx.body, self.ccx.tcx);
56 if base_ty.ty.is_union() && Q::in_any_value_of_ty(self.ccx, base_ty.ty) {
57 value = true;
58 break;
59 }
60 }
61 }
62
e74abb32 63 match (value, place.as_ref()) {
dfeec247 64 (true, mir::PlaceRef { local, .. }) => {
3c0e092e 65 self.state.qualif.insert(local);
e74abb32
XL
66 }
67
68 // For now, we do not clear the qualif if a local is overwritten in full by
69 // an unqualified rvalue (e.g. `y = 5`). This is to be consistent
70 // with aggregates where we overwrite all fields with assignments, which would not
71 // get this feature.
dfeec247 72 (false, mir::PlaceRef { local: _, projection: &[] }) => {
3c0e092e 73 // self.state.qualif.remove(*local);
e74abb32
XL
74 }
75
76 _ => {}
77 }
78 }
79
80 fn apply_call_return_effect(
81 &mut self,
82 _block: BasicBlock,
ba9703b0
XL
83 _func: &mir::Operand<'tcx>,
84 _args: &[mir::Operand<'tcx>],
85 return_place: mir::Place<'tcx>,
e74abb32 86 ) {
ba9703b0
XL
87 // We cannot reason about another function's internals, so use conservative type-based
88 // qualification for the result of a function call.
f9f354fc
XL
89 let return_ty = return_place.ty(self.ccx.body, self.ccx.tcx).ty;
90 let qualif = Q::in_any_value_of_ty(self.ccx, return_ty);
ba9703b0 91
e74abb32 92 if !return_place.is_indirect() {
ba9703b0 93 self.assign_qualif_direct(&return_place, qualif);
e74abb32
XL
94 }
95 }
3c0e092e
XL
96
97 fn address_of_allows_mutation(&self, _mt: mir::Mutability, _place: mir::Place<'tcx>) -> bool {
98 // Exact set of permissions granted by AddressOf is undecided. Conservatively assume that
99 // it might allow mutation until resolution of #56604.
100 true
101 }
102
103 fn ref_allows_mutation(&self, kind: mir::BorrowKind, place: mir::Place<'tcx>) -> bool {
104 match kind {
105 mir::BorrowKind::Mut { .. } => true,
106 mir::BorrowKind::Shared | mir::BorrowKind::Shallow | mir::BorrowKind::Unique => {
107 self.shared_borrow_allows_mutation(place)
108 }
109 }
110 }
111
112 /// `&` only allow mutation if the borrowed place is `!Freeze`.
113 ///
114 /// This assumes that it is UB to take the address of a struct field whose type is
115 /// `Freeze`, then use pointer arithmetic to derive a pointer to a *different* field of
116 /// that same struct whose type is `!Freeze`. If we decide that this is not UB, we will
117 /// have to check the type of the borrowed **local** instead of the borrowed **place**
118 /// below. See [rust-lang/unsafe-code-guidelines#134].
119 ///
120 /// [rust-lang/unsafe-code-guidelines#134]: https://github.com/rust-lang/unsafe-code-guidelines/issues/134
121 fn shared_borrow_allows_mutation(&self, place: mir::Place<'tcx>) -> bool {
122 !place
123 .ty(self.ccx.body, self.ccx.tcx)
124 .ty
125 .is_freeze(self.ccx.tcx.at(DUMMY_SP), self.ccx.param_env)
126 }
e74abb32
XL
127}
128
129impl<Q> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx, Q>
130where
131 Q: Qualif,
132{
133 fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
134 self.super_operand(operand, location);
135
136 if !Q::IS_CLEARED_ON_MOVE {
137 return;
138 }
139
140 // If a local with no projections is moved from (e.g. `x` in `y = x`), record that
141 // it no longer needs to be dropped.
142 if let mir::Operand::Move(place) = operand {
143 if let Some(local) = place.as_local() {
3c0e092e
XL
144 // For backward compatibility with the MaybeMutBorrowedLocals used in an earlier
145 // implementation we retain qualif if a local had been borrowed before. This might
146 // not be strictly necessary since the local is no longer initialized.
147 if !self.state.borrow.contains(local) {
148 self.state.qualif.remove(local);
149 }
e74abb32
XL
150 }
151 }
152 }
153
154 fn visit_assign(
155 &mut self,
156 place: &mir::Place<'tcx>,
157 rvalue: &mir::Rvalue<'tcx>,
158 location: Location,
159 ) {
3c0e092e
XL
160 let qualif =
161 qualifs::in_rvalue::<Q, _>(self.ccx, &mut |l| self.state.qualif.contains(l), rvalue);
e74abb32
XL
162 if !place.is_indirect() {
163 self.assign_qualif_direct(place, qualif);
164 }
165
166 // We need to assign qualifs to the left-hand side before visiting `rvalue` since
167 // qualifs can be cleared on move.
168 self.super_assign(place, rvalue, location);
169 }
170
3c0e092e
XL
171 fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
172 self.super_rvalue(rvalue, location);
173
174 match rvalue {
175 mir::Rvalue::AddressOf(mt, borrowed_place) => {
176 if !borrowed_place.is_indirect()
177 && self.address_of_allows_mutation(*mt, *borrowed_place)
178 {
179 let place_ty = borrowed_place.ty(self.ccx.body, self.ccx.tcx).ty;
180 if Q::in_any_value_of_ty(self.ccx, place_ty) {
181 self.state.qualif.insert(borrowed_place.local);
182 self.state.borrow.insert(borrowed_place.local);
183 }
184 }
185 }
186
187 mir::Rvalue::Ref(_, kind, borrowed_place) => {
188 if !borrowed_place.is_indirect() && self.ref_allows_mutation(*kind, *borrowed_place)
189 {
190 let place_ty = borrowed_place.ty(self.ccx.body, self.ccx.tcx).ty;
191 if Q::in_any_value_of_ty(self.ccx, place_ty) {
192 self.state.qualif.insert(borrowed_place.local);
193 self.state.borrow.insert(borrowed_place.local);
194 }
195 }
196 }
197
198 mir::Rvalue::Cast(..)
199 | mir::Rvalue::ShallowInitBox(..)
200 | mir::Rvalue::Use(..)
201 | mir::Rvalue::ThreadLocalRef(..)
202 | mir::Rvalue::Repeat(..)
203 | mir::Rvalue::Len(..)
204 | mir::Rvalue::BinaryOp(..)
205 | mir::Rvalue::CheckedBinaryOp(..)
206 | mir::Rvalue::NullaryOp(..)
207 | mir::Rvalue::UnaryOp(..)
208 | mir::Rvalue::Discriminant(..)
209 | mir::Rvalue::Aggregate(..) => {}
210 }
211 }
212
213 fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
214 match statement.kind {
215 StatementKind::StorageDead(local) => {
216 self.state.qualif.remove(local);
217 self.state.borrow.remove(local);
218 }
219 _ => self.super_statement(statement, location),
220 }
221 }
222
f035d41b 223 fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
e74abb32
XL
224 // The effect of assignment to the return place in `TerminatorKind::Call` is not applied
225 // here; that occurs in `apply_call_return_effect`.
226
f035d41b 227 if let mir::TerminatorKind::DropAndReplace { value, place, .. } = &terminator.kind {
ba9703b0 228 let qualif = qualifs::in_operand::<Q, _>(
f9f354fc 229 self.ccx,
3c0e092e 230 &mut |l| self.state.qualif.contains(l),
ba9703b0
XL
231 value,
232 );
233
f035d41b
XL
234 if !place.is_indirect() {
235 self.assign_qualif_direct(place, qualif);
e74abb32
XL
236 }
237 }
238
3c0e092e
XL
239 // We ignore borrow on drop because custom drop impls are not allowed in consts.
240 // FIXME: Reconsider if accounting for borrows in drops is necessary for const drop.
241
e74abb32
XL
242 // We need to assign qualifs to the dropped location before visiting the operand that
243 // replaces it since qualifs can be cleared on move.
f035d41b 244 self.super_terminator(terminator, location);
e74abb32
XL
245 }
246}
247
248/// The dataflow analysis used to propagate qualifs on arbitrary CFGs.
249pub(super) struct FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> {
f9f354fc 250 ccx: &'a ConstCx<'mir, 'tcx>,
e74abb32
XL
251 _qualif: PhantomData<Q>,
252}
253
254impl<'a, 'mir, 'tcx, Q> FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q>
255where
256 Q: Qualif,
257{
f9f354fc
XL
258 pub(super) fn new(_: Q, ccx: &'a ConstCx<'mir, 'tcx>) -> Self {
259 FlowSensitiveAnalysis { ccx, _qualif: PhantomData }
e74abb32
XL
260 }
261
3c0e092e 262 fn transfer_function(&self, state: &'a mut State) -> TransferFunction<'a, 'mir, 'tcx, Q> {
f9f354fc 263 TransferFunction::<Q>::new(self.ccx, state)
e74abb32
XL
264 }
265}
266
3c0e092e
XL
267#[derive(Debug, PartialEq, Eq)]
268pub(super) struct State {
269 /// Describes whether a local contains qualif.
270 pub qualif: BitSet<Local>,
271 /// Describes whether a local's address escaped and it might become qualified as a result an
272 /// indirect mutation.
273 pub borrow: BitSet<Local>,
274}
275
276impl Clone for State {
277 fn clone(&self) -> Self {
278 State { qualif: self.qualif.clone(), borrow: self.borrow.clone() }
279 }
280
281 // Data flow engine when possible uses `clone_from` for domain values.
282 // Providing an implementation will avoid some intermediate memory allocations.
283 fn clone_from(&mut self, other: &Self) {
284 self.qualif.clone_from(&other.qualif);
285 self.borrow.clone_from(&other.borrow);
286 }
287}
288
289impl State {
290 #[inline]
291 pub(super) fn contains(&self, local: Local) -> bool {
292 self.qualif.contains(local)
293 }
294}
295
296impl<C> DebugWithContext<C> for State {
297 fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298 f.write_str("qualif: ")?;
299 self.qualif.fmt_with(ctxt, f)?;
300 f.write_str(" borrow: ")?;
301 self.borrow.fmt_with(ctxt, f)?;
302 Ok(())
303 }
304
305 fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
306 if self == old {
307 return Ok(());
308 }
309
310 if self.qualif != old.qualif {
311 f.write_str("qualif: ")?;
312 self.qualif.fmt_diff_with(&old.qualif, ctxt, f)?;
313 f.write_str("\n")?;
314 }
315
316 if self.borrow != old.borrow {
317 f.write_str("borrow: ")?;
318 self.qualif.fmt_diff_with(&old.borrow, ctxt, f)?;
319 f.write_str("\n")?;
320 }
321
322 Ok(())
323 }
324}
325
326impl JoinSemiLattice for State {
327 fn join(&mut self, other: &Self) -> bool {
328 self.qualif.join(&other.qualif) || self.borrow.join(&other.borrow)
329 }
330}
331
c295e0f8 332impl<Q> rustc_mir_dataflow::AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
e74abb32
XL
333where
334 Q: Qualif,
335{
3c0e092e 336 type Domain = State;
e74abb32
XL
337
338 const NAME: &'static str = Q::ANALYSIS_NAME;
339
1b1a35ee 340 fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
3c0e092e
XL
341 State {
342 qualif: BitSet::new_empty(body.local_decls.len()),
343 borrow: BitSet::new_empty(body.local_decls.len()),
344 }
e74abb32
XL
345 }
346
1b1a35ee 347 fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut Self::Domain) {
e74abb32
XL
348 self.transfer_function(state).initialize_state();
349 }
dfeec247 350}
e74abb32 351
c295e0f8 352impl<Q> rustc_mir_dataflow::Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
dfeec247
XL
353where
354 Q: Qualif,
355{
e74abb32
XL
356 fn apply_statement_effect(
357 &self,
1b1a35ee 358 state: &mut Self::Domain,
e74abb32
XL
359 statement: &mir::Statement<'tcx>,
360 location: Location,
361 ) {
362 self.transfer_function(state).visit_statement(statement, location);
363 }
364
365 fn apply_terminator_effect(
366 &self,
1b1a35ee 367 state: &mut Self::Domain,
e74abb32
XL
368 terminator: &mir::Terminator<'tcx>,
369 location: Location,
370 ) {
371 self.transfer_function(state).visit_terminator(terminator, location);
372 }
373
374 fn apply_call_return_effect(
375 &self,
1b1a35ee 376 state: &mut Self::Domain,
e74abb32
XL
377 block: BasicBlock,
378 func: &mir::Operand<'tcx>,
379 args: &[mir::Operand<'tcx>],
ba9703b0 380 return_place: mir::Place<'tcx>,
e74abb32
XL
381 ) {
382 self.transfer_function(state).apply_call_return_effect(block, func, args, return_place)
383 }
384}