rustc_const_eval/check_consts/
resolver.rs1use std::fmt;
6use std::marker::PhantomData;
7
8use rustc_index::bit_set::MixedBitSet;
9use rustc_middle::mir::visit::Visitor;
10use rustc_middle::mir::{
11 self, BasicBlock, CallReturnPlaces, Local, Location, Statement, StatementKind, TerminatorEdges,
12};
13use rustc_mir_dataflow::fmt::DebugWithContext;
14use rustc_mir_dataflow::{Analysis, JoinSemiLattice};
15
16use super::{ConstCx, Qualif, qualifs};
17
18struct TransferFunction<'mir, 'tcx, Q> {
26 ccx: &'mir ConstCx<'mir, 'tcx>,
27 state: &'mir mut State,
28 _qualif: PhantomData<Q>,
29}
30
31impl<'mir, 'tcx, Q> TransferFunction<'mir, 'tcx, Q>
32where
33 Q: Qualif,
34{
35 fn new(ccx: &'mir ConstCx<'mir, 'tcx>, state: &'mir mut State) -> Self {
36 TransferFunction { ccx, state, _qualif: PhantomData }
37 }
38
39 fn initialize_state(&mut self) {
40 self.state.qualif.clear();
41 self.state.borrow.clear();
42
43 for arg in self.ccx.body.args_iter() {
44 let arg_ty = self.ccx.body.local_decls[arg].ty;
45 if Q::in_any_value_of_ty(self.ccx, arg_ty) {
46 self.state.qualif.insert(arg);
47 }
48 }
49 }
50
51 fn assign_qualif_direct(&mut self, place: &mir::Place<'tcx>, mut value: bool) {
52 if true {
if !!place.is_indirect() {
::core::panicking::panic("assertion failed: !place.is_indirect()")
};
};debug_assert!(!place.is_indirect());
53
54 if !value {
55 for (base, _elem) in place.iter_projections() {
56 let base_ty = base.ty(self.ccx.body, self.ccx.tcx);
57 if base_ty.ty.is_union() && Q::in_any_value_of_ty(self.ccx, base_ty.ty) {
58 value = true;
59 break;
60 }
61 }
62 }
63
64 match (value, place.as_ref()) {
65 (true, mir::PlaceRef { local, .. }) => {
66 self.state.qualif.insert(local);
67 }
68
69 (false, mir::PlaceRef { local: _, projection: &[] }) => {
74 }
76
77 _ => {}
78 }
79 }
80
81 fn apply_call_return_effect(
82 &mut self,
83 _block: BasicBlock,
84 return_places: CallReturnPlaces<'_, 'tcx>,
85 ) {
86 return_places.for_each(|place| {
87 let return_ty = place.ty(self.ccx.body, self.ccx.tcx).ty;
90 let qualif = Q::in_any_value_of_ty(self.ccx, return_ty);
91
92 if !place.is_indirect() {
93 self.assign_qualif_direct(&place, qualif);
94 }
95 });
96 }
97
98 fn address_of_allows_mutation(&self) -> bool {
99 true
102 }
103
104 fn ref_allows_mutation(&self, kind: mir::BorrowKind, place: mir::Place<'tcx>) -> bool {
105 match kind {
106 mir::BorrowKind::Mut { .. } => true,
107 mir::BorrowKind::Shared | mir::BorrowKind::Fake(_) => {
108 self.shared_borrow_allows_mutation(place)
109 }
110 }
111 }
112
113 fn shared_borrow_allows_mutation(&self, place: mir::Place<'tcx>) -> bool {
123 !place.ty(self.ccx.body, self.ccx.tcx).ty.is_freeze(self.ccx.tcx, self.ccx.typing_env)
124 }
125}
126
127impl<'tcx, Q> Visitor<'tcx> for TransferFunction<'_, 'tcx, Q>
128where
129 Q: Qualif,
130{
131 fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
132 self.super_operand(operand, location);
133
134 if !Q::IS_CLEARED_ON_MOVE {
135 return;
136 }
137
138 if let mir::Operand::Move(place) = operand
141 && let Some(local) = place.as_local()
142 {
143 if !self.state.borrow.contains(local) {
147 self.state.qualif.remove(local);
148 }
149 }
150 }
151
152 fn visit_assign(
153 &mut self,
154 place: &mir::Place<'tcx>,
155 rvalue: &mir::Rvalue<'tcx>,
156 location: Location,
157 ) {
158 let qualif =
159 qualifs::in_rvalue::<Q, _>(self.ccx, &mut |l| self.state.qualif.contains(l), rvalue);
160 if !place.is_indirect() {
161 self.assign_qualif_direct(place, qualif);
162 }
163
164 self.super_assign(place, rvalue, location);
167 }
168
169 fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
170 self.super_rvalue(rvalue, location);
171
172 match rvalue {
173 mir::Rvalue::RawPtr(_mt, borrowed_place) => {
174 if !borrowed_place.is_indirect() && self.address_of_allows_mutation() {
175 let place_ty = borrowed_place.ty(self.ccx.body, self.ccx.tcx).ty;
176 if Q::in_any_value_of_ty(self.ccx, place_ty) {
177 self.state.qualif.insert(borrowed_place.local);
178 self.state.borrow.insert(borrowed_place.local);
179 }
180 }
181 }
182
183 mir::Rvalue::Ref(_, kind, borrowed_place) => {
184 if !borrowed_place.is_indirect() && self.ref_allows_mutation(*kind, *borrowed_place)
185 {
186 let place_ty = borrowed_place.ty(self.ccx.body, self.ccx.tcx).ty;
187 if Q::in_any_value_of_ty(self.ccx, place_ty) {
188 self.state.qualif.insert(borrowed_place.local);
189 self.state.borrow.insert(borrowed_place.local);
190 }
191 }
192 }
193
194 mir::Rvalue::Reborrow(target, mutability, borrowed_place) => {
195 if !borrowed_place.is_indirect()
198 && (mutability.is_mut() || !target.is_freeze(self.ccx.tcx, self.ccx.typing_env))
199 {
200 if Q::in_any_value_of_ty(self.ccx, *target) {
201 self.state.qualif.insert(borrowed_place.local);
202 self.state.borrow.insert(borrowed_place.local);
203 }
204 }
205 }
206
207 mir::Rvalue::Cast(..)
208 | mir::Rvalue::Use(..)
209 | mir::Rvalue::CopyForDeref(..)
210 | mir::Rvalue::ThreadLocalRef(..)
211 | mir::Rvalue::Repeat(..)
212 | mir::Rvalue::BinaryOp(..)
213 | mir::Rvalue::UnaryOp(..)
214 | mir::Rvalue::Discriminant(..)
215 | mir::Rvalue::Aggregate(..)
216 | mir::Rvalue::WrapUnsafeBinder(..) => {}
217 }
218 }
219
220 fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
221 match statement.kind {
222 StatementKind::StorageDead(local) => {
223 self.state.qualif.remove(local);
224 self.state.borrow.remove(local);
225 }
226 _ => self.super_statement(statement, location),
227 }
228 }
229
230 fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
231 self.super_terminator(terminator, location);
237 }
238}
239
240pub(super) struct FlowSensitiveAnalysis<'mir, 'tcx, Q> {
242 ccx: &'mir ConstCx<'mir, 'tcx>,
243 _qualif: PhantomData<Q>,
244}
245
246impl<'mir, 'tcx, Q> FlowSensitiveAnalysis<'mir, 'tcx, Q>
247where
248 Q: Qualif,
249{
250 pub(super) fn new(_: Q, ccx: &'mir ConstCx<'mir, 'tcx>) -> Self {
251 FlowSensitiveAnalysis { ccx, _qualif: PhantomData }
252 }
253
254 fn transfer_function(&self, state: &'mir mut State) -> TransferFunction<'mir, 'tcx, Q> {
255 TransferFunction::<Q>::new(self.ccx, state)
256 }
257}
258
259#[derive(#[automatically_derived]
impl ::core::fmt::Debug for State {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "State",
"qualif", &self.qualif, "borrow", &&self.borrow)
}
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for State {
#[inline]
fn eq(&self, other: &State) -> bool {
self.qualif == other.qualif && self.borrow == other.borrow
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for State {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<MixedBitSet<Local>>;
let _: ::core::cmp::AssertParamIsEq<MixedBitSet<Local>>;
}
}Eq)]
260pub(super) struct State {
263 pub qualif: MixedBitSet<Local>,
265 pub borrow: MixedBitSet<Local>,
268}
269
270impl Clone for State {
271 fn clone(&self) -> Self {
272 State { qualif: self.qualif.clone(), borrow: self.borrow.clone() }
273 }
274
275 fn clone_from(&mut self, other: &Self) {
278 self.qualif.clone_from(&other.qualif);
279 self.borrow.clone_from(&other.borrow);
280 }
281}
282
283impl State {
284 #[inline]
285 pub(super) fn contains(&self, local: Local) -> bool {
286 self.qualif.contains(local)
287 }
288}
289
290impl<C> DebugWithContext<C> for State {
291 fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292 f.write_str("qualif: ")?;
293 self.qualif.fmt_with(ctxt, f)?;
294 f.write_str(" borrow: ")?;
295 self.borrow.fmt_with(ctxt, f)?;
296 Ok(())
297 }
298
299 fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300 if self == old {
301 return Ok(());
302 }
303
304 if self.qualif != old.qualif {
305 f.write_str("qualif: ")?;
306 self.qualif.fmt_diff_with(&old.qualif, ctxt, f)?;
307 f.write_str("\n")?;
308 }
309
310 if self.borrow != old.borrow {
311 f.write_str("borrow: ")?;
312 self.qualif.fmt_diff_with(&old.borrow, ctxt, f)?;
313 f.write_str("\n")?;
314 }
315
316 Ok(())
317 }
318}
319
320impl JoinSemiLattice for State {
321 fn join(&mut self, other: &Self) -> bool {
322 self.qualif.join(&other.qualif) || self.borrow.join(&other.borrow)
323 }
324}
325
326impl<'tcx, Q> Analysis<'tcx> for FlowSensitiveAnalysis<'_, 'tcx, Q>
327where
328 Q: Qualif,
329{
330 type Domain = State;
331
332 const NAME: &'static str = Q::ANALYSIS_NAME;
333
334 fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
335 State {
336 qualif: MixedBitSet::new_empty(body.local_decls.len()),
337 borrow: MixedBitSet::new_empty(body.local_decls.len()),
338 }
339 }
340
341 fn initialize_start_block(&self, _body: &mir::Body<'tcx>, state: &mut Self::Domain) {
342 self.transfer_function(state).initialize_state();
343 }
344
345 fn apply_primary_statement_effect(
346 &self,
347 state: &mut Self::Domain,
348 statement: &mir::Statement<'tcx>,
349 location: Location,
350 ) {
351 self.transfer_function(state).visit_statement(statement, location);
352 }
353
354 fn apply_primary_terminator_effect<'mir>(
355 &self,
356 state: &mut Self::Domain,
357 terminator: &'mir mir::Terminator<'tcx>,
358 location: Location,
359 ) -> TerminatorEdges<'mir, 'tcx> {
360 self.transfer_function(state).visit_terminator(terminator, location);
361 terminator.edges()
362 }
363
364 fn apply_call_return_effect(
365 &self,
366 state: &mut Self::Domain,
367 block: BasicBlock,
368 return_places: CallReturnPlaces<'_, 'tcx>,
369 ) {
370 self.transfer_function(state).apply_call_return_effect(block, return_places)
371 }
372}