]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir/src/transform/check_consts/resolver.rs
Rollup merge of #82234 - GuillaumeGomez:remove-query-param-on-esc, r=Nemo157
[rust.git] / compiler / rustc_mir / src / transform / check_consts / resolver.rs
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
5 use rustc_index::bit_set::BitSet;
6 use rustc_middle::mir::visit::Visitor;
7 use rustc_middle::mir::{self, BasicBlock, Local, Location};
8
9 use std::marker::PhantomData;
10
11 use super::{qualifs, ConstCx, Qualif};
12 use crate::dataflow;
13
14 /// A `Visitor` that propagates qualifs between locals. This defines the transfer function of
15 /// `FlowSensitiveAnalysis`.
16 ///
17 /// This transfer does nothing when encountering an indirect assignment. Consumers should rely on
18 /// the `MaybeMutBorrowedLocals` dataflow pass to see if a `Local` may have become qualified via
19 /// an indirect assignment or function call.
20 struct TransferFunction<'a, 'mir, 'tcx, Q> {
21     ccx: &'a ConstCx<'mir, 'tcx>,
22     qualifs_per_local: &'a mut BitSet<Local>,
23
24     _qualif: PhantomData<Q>,
25 }
26
27 impl<Q> TransferFunction<'a, 'mir, 'tcx, Q>
28 where
29     Q: Qualif,
30 {
31     fn new(ccx: &'a ConstCx<'mir, 'tcx>, qualifs_per_local: &'a mut BitSet<Local>) -> Self {
32         TransferFunction { ccx, qualifs_per_local, _qualif: PhantomData }
33     }
34
35     fn initialize_state(&mut self) {
36         self.qualifs_per_local.clear();
37
38         for arg in self.ccx.body.args_iter() {
39             let arg_ty = self.ccx.body.local_decls[arg].ty;
40             if Q::in_any_value_of_ty(self.ccx, arg_ty) {
41                 self.qualifs_per_local.insert(arg);
42             }
43         }
44     }
45
46     fn assign_qualif_direct(&mut self, place: &mir::Place<'tcx>, value: bool) {
47         debug_assert!(!place.is_indirect());
48
49         match (value, place.as_ref()) {
50             (true, mir::PlaceRef { local, .. }) => {
51                 self.qualifs_per_local.insert(local);
52             }
53
54             // For now, we do not clear the qualif if a local is overwritten in full by
55             // an unqualified rvalue (e.g. `y = 5`). This is to be consistent
56             // with aggregates where we overwrite all fields with assignments, which would not
57             // get this feature.
58             (false, mir::PlaceRef { local: _, projection: &[] }) => {
59                 // self.qualifs_per_local.remove(*local);
60             }
61
62             _ => {}
63         }
64     }
65
66     fn apply_call_return_effect(
67         &mut self,
68         _block: BasicBlock,
69         _func: &mir::Operand<'tcx>,
70         _args: &[mir::Operand<'tcx>],
71         return_place: mir::Place<'tcx>,
72     ) {
73         // We cannot reason about another function's internals, so use conservative type-based
74         // qualification for the result of a function call.
75         let return_ty = return_place.ty(self.ccx.body, self.ccx.tcx).ty;
76         let qualif = Q::in_any_value_of_ty(self.ccx, return_ty);
77
78         if !return_place.is_indirect() {
79             self.assign_qualif_direct(&return_place, qualif);
80         }
81     }
82 }
83
84 impl<Q> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx, Q>
85 where
86     Q: Qualif,
87 {
88     fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
89         self.super_operand(operand, location);
90
91         if !Q::IS_CLEARED_ON_MOVE {
92             return;
93         }
94
95         // If a local with no projections is moved from (e.g. `x` in `y = x`), record that
96         // it no longer needs to be dropped.
97         if let mir::Operand::Move(place) = operand {
98             if let Some(local) = place.as_local() {
99                 self.qualifs_per_local.remove(local);
100             }
101         }
102     }
103
104     fn visit_assign(
105         &mut self,
106         place: &mir::Place<'tcx>,
107         rvalue: &mir::Rvalue<'tcx>,
108         location: Location,
109     ) {
110         let qualif = qualifs::in_rvalue::<Q, _>(
111             self.ccx,
112             &mut |l| self.qualifs_per_local.contains(l),
113             rvalue,
114         );
115         if !place.is_indirect() {
116             self.assign_qualif_direct(place, qualif);
117         }
118
119         // We need to assign qualifs to the left-hand side before visiting `rvalue` since
120         // qualifs can be cleared on move.
121         self.super_assign(place, rvalue, location);
122     }
123
124     fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
125         // The effect of assignment to the return place in `TerminatorKind::Call` is not applied
126         // here; that occurs in `apply_call_return_effect`.
127
128         if let mir::TerminatorKind::DropAndReplace { value, place, .. } = &terminator.kind {
129             let qualif = qualifs::in_operand::<Q, _>(
130                 self.ccx,
131                 &mut |l| self.qualifs_per_local.contains(l),
132                 value,
133             );
134
135             if !place.is_indirect() {
136                 self.assign_qualif_direct(place, qualif);
137             }
138         }
139
140         // We need to assign qualifs to the dropped location before visiting the operand that
141         // replaces it since qualifs can be cleared on move.
142         self.super_terminator(terminator, location);
143     }
144 }
145
146 /// The dataflow analysis used to propagate qualifs on arbitrary CFGs.
147 pub(super) struct FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q> {
148     ccx: &'a ConstCx<'mir, 'tcx>,
149     _qualif: PhantomData<Q>,
150 }
151
152 impl<'a, 'mir, 'tcx, Q> FlowSensitiveAnalysis<'a, 'mir, 'tcx, Q>
153 where
154     Q: Qualif,
155 {
156     pub(super) fn new(_: Q, ccx: &'a ConstCx<'mir, 'tcx>) -> Self {
157         FlowSensitiveAnalysis { ccx, _qualif: PhantomData }
158     }
159
160     fn transfer_function(
161         &self,
162         state: &'a mut BitSet<Local>,
163     ) -> TransferFunction<'a, 'mir, 'tcx, Q> {
164         TransferFunction::<Q>::new(self.ccx, state)
165     }
166 }
167
168 impl<Q> dataflow::AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
169 where
170     Q: Qualif,
171 {
172     type Domain = BitSet<Local>;
173
174     const NAME: &'static str = Q::ANALYSIS_NAME;
175
176     fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
177         BitSet::new_empty(body.local_decls.len())
178     }
179
180     fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut Self::Domain) {
181         self.transfer_function(state).initialize_state();
182     }
183 }
184
185 impl<Q> dataflow::Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
186 where
187     Q: Qualif,
188 {
189     fn apply_statement_effect(
190         &self,
191         state: &mut Self::Domain,
192         statement: &mir::Statement<'tcx>,
193         location: Location,
194     ) {
195         self.transfer_function(state).visit_statement(statement, location);
196     }
197
198     fn apply_terminator_effect(
199         &self,
200         state: &mut Self::Domain,
201         terminator: &mir::Terminator<'tcx>,
202         location: Location,
203     ) {
204         self.transfer_function(state).visit_terminator(terminator, location);
205     }
206
207     fn apply_call_return_effect(
208         &self,
209         state: &mut Self::Domain,
210         block: BasicBlock,
211         func: &mir::Operand<'tcx>,
212         args: &[mir::Operand<'tcx>],
213         return_place: mir::Place<'tcx>,
214     ) {
215         self.transfer_function(state).apply_call_return_effect(block, func, args, return_place)
216     }
217 }