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