1 //! This module provides a framework on top of the normal MIR dataflow framework to simplify the
2 //! implementation of analyses that track information about the values stored in certain places.
3 //! We are using the term "place" here to refer to a `mir::Place` (a place expression) instead of
4 //! an `interpret::Place` (a memory location).
5 //!
6 //! The default methods of [`ValueAnalysis`] (prefixed with `super_` instead of `handle_`)
7 //! provide some behavior that should be valid for all abstract domains that are based only on the
8 //! value stored in a certain place. On top of these default rules, an implementation should
9 //! override some of the `handle_` methods. For an example, see `ConstAnalysis`.
10 //!
11 //! An implementation must also provide a [`Map`]. Before the analysis begins, all places that
12 //! should be tracked during the analysis must be registered. During the analysis, no new places
13 //! can be registered. The [`State`] can be queried to retrieve the abstract value stored for a
14 //! certain place by passing the map.
15 //!
16 //! This framework is currently experimental. Originally, it supported shared references and enum
17 //! variants. However, it was discovered that both of these were unsound, and especially references
18 //! had subtle but serious issues. In the future, they could be added back in, but we should clarify
19 //! the rules for optimizations that rely on the aliasing model first.
20 //!
21 //!
22 //! # Notes
23 //!
24 //! - The bottom state denotes uninitialized memory. Because we are only doing a sound approximation
25 //! of the actual execution, we can also use this state for places where access would be UB.
26 //!
27 //! - The assignment logic in `State::insert_place_idx` assumes that the places are non-overlapping,
28 //! or identical. Note that this refers to place expressions, not memory locations.
29 //!
30 //! - Currently, places that have their reference taken cannot be tracked. Although this would be
31 //! possible, it has to rely on some aliasing model, which we are not ready to commit to yet.
32 //! Because of that, we can assume that the only way to change the value behind a tracked place is
33 //! by direct assignment.
34
35 use std::collections::VecDeque;
36 use std::fmt::{Debug, Formatter};
37 use std::ops::Range;
38
39 use rustc_data_structures::fx::FxHashMap;
40 use rustc_data_structures::stack::ensure_sufficient_stack;
41 use rustc_index::bit_set::BitSet;
42 use rustc_index::{IndexSlice, IndexVec};
43 use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
44 use rustc_middle::mir::*;
45 use rustc_middle::ty::{self, Ty, TyCtxt};
46 use rustc_target::abi::{FieldIdx, VariantIdx};
47
48 use crate::lattice::{HasBottom, HasTop};
49 use crate::{
50 fmt::DebugWithContext, Analysis, AnalysisDomain, CallReturnPlaces, JoinSemiLattice,
51 SwitchIntEdgeEffects,
52 };
53
54 pub trait ValueAnalysis<'tcx> {
55 /// For each place of interest, the analysis tracks a value of the given type.
56 type Value: Clone + JoinSemiLattice + HasBottom + HasTop;
57
58 const NAME: &'static str;
59
map(&self) -> &Map60 fn map(&self) -> ⤅
61
handle_statement(&self, statement: &Statement<'tcx>, state: &mut State<Self::Value>)62 fn handle_statement(&self, statement: &Statement<'tcx>, state: &mut State<Self::Value>) {
63 self.super_statement(statement, state)
64 }
65
super_statement(&self, statement: &Statement<'tcx>, state: &mut State<Self::Value>)66 fn super_statement(&self, statement: &Statement<'tcx>, state: &mut State<Self::Value>) {
67 match &statement.kind {
68 StatementKind::Assign(box (place, rvalue)) => {
69 self.handle_assign(*place, rvalue, state);
70 }
71 StatementKind::SetDiscriminant { box place, variant_index } => {
72 self.handle_set_discriminant(*place, *variant_index, state);
73 }
74 StatementKind::Intrinsic(box intrinsic) => {
75 self.handle_intrinsic(intrinsic, state);
76 }
77 StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => {
78 // StorageLive leaves the local in an uninitialized state.
79 // StorageDead makes it UB to access the local afterwards.
80 state.flood_with(Place::from(*local).as_ref(), self.map(), Self::Value::BOTTOM);
81 }
82 StatementKind::Deinit(box place) => {
83 // Deinit makes the place uninitialized.
84 state.flood_with(place.as_ref(), self.map(), Self::Value::BOTTOM);
85 }
86 StatementKind::Retag(..) => {
87 // We don't track references.
88 }
89 StatementKind::ConstEvalCounter
90 | StatementKind::Nop
91 | StatementKind::FakeRead(..)
92 | StatementKind::PlaceMention(..)
93 | StatementKind::Coverage(..)
94 | StatementKind::AscribeUserType(..) => (),
95 }
96 }
97
handle_set_discriminant( &self, place: Place<'tcx>, variant_index: VariantIdx, state: &mut State<Self::Value>, )98 fn handle_set_discriminant(
99 &self,
100 place: Place<'tcx>,
101 variant_index: VariantIdx,
102 state: &mut State<Self::Value>,
103 ) {
104 self.super_set_discriminant(place, variant_index, state)
105 }
106
super_set_discriminant( &self, place: Place<'tcx>, _variant_index: VariantIdx, state: &mut State<Self::Value>, )107 fn super_set_discriminant(
108 &self,
109 place: Place<'tcx>,
110 _variant_index: VariantIdx,
111 state: &mut State<Self::Value>,
112 ) {
113 state.flood_discr(place.as_ref(), self.map());
114 }
115
handle_intrinsic( &self, intrinsic: &NonDivergingIntrinsic<'tcx>, state: &mut State<Self::Value>, )116 fn handle_intrinsic(
117 &self,
118 intrinsic: &NonDivergingIntrinsic<'tcx>,
119 state: &mut State<Self::Value>,
120 ) {
121 self.super_intrinsic(intrinsic, state);
122 }
123
super_intrinsic( &self, intrinsic: &NonDivergingIntrinsic<'tcx>, _state: &mut State<Self::Value>, )124 fn super_intrinsic(
125 &self,
126 intrinsic: &NonDivergingIntrinsic<'tcx>,
127 _state: &mut State<Self::Value>,
128 ) {
129 match intrinsic {
130 NonDivergingIntrinsic::Assume(..) => {
131 // Could use this, but ignoring it is sound.
132 }
133 NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping {
134 dst: _,
135 src: _,
136 count: _,
137 }) => {
138 // This statement represents `*dst = *src`, `count` times.
139 }
140 }
141 }
142
handle_assign( &self, target: Place<'tcx>, rvalue: &Rvalue<'tcx>, state: &mut State<Self::Value>, )143 fn handle_assign(
144 &self,
145 target: Place<'tcx>,
146 rvalue: &Rvalue<'tcx>,
147 state: &mut State<Self::Value>,
148 ) {
149 self.super_assign(target, rvalue, state)
150 }
151
super_assign( &self, target: Place<'tcx>, rvalue: &Rvalue<'tcx>, state: &mut State<Self::Value>, )152 fn super_assign(
153 &self,
154 target: Place<'tcx>,
155 rvalue: &Rvalue<'tcx>,
156 state: &mut State<Self::Value>,
157 ) {
158 let result = self.handle_rvalue(rvalue, state);
159 state.assign(target.as_ref(), result, self.map());
160 }
161
handle_rvalue( &self, rvalue: &Rvalue<'tcx>, state: &mut State<Self::Value>, ) -> ValueOrPlace<Self::Value>162 fn handle_rvalue(
163 &self,
164 rvalue: &Rvalue<'tcx>,
165 state: &mut State<Self::Value>,
166 ) -> ValueOrPlace<Self::Value> {
167 self.super_rvalue(rvalue, state)
168 }
169
super_rvalue( &self, rvalue: &Rvalue<'tcx>, state: &mut State<Self::Value>, ) -> ValueOrPlace<Self::Value>170 fn super_rvalue(
171 &self,
172 rvalue: &Rvalue<'tcx>,
173 state: &mut State<Self::Value>,
174 ) -> ValueOrPlace<Self::Value> {
175 match rvalue {
176 Rvalue::Use(operand) => self.handle_operand(operand, state),
177 Rvalue::CopyForDeref(place) => self.handle_operand(&Operand::Copy(*place), state),
178 Rvalue::Ref(..) | Rvalue::AddressOf(..) => {
179 // We don't track such places.
180 ValueOrPlace::TOP
181 }
182 Rvalue::Repeat(..)
183 | Rvalue::ThreadLocalRef(..)
184 | Rvalue::Len(..)
185 | Rvalue::Cast(..)
186 | Rvalue::BinaryOp(..)
187 | Rvalue::CheckedBinaryOp(..)
188 | Rvalue::NullaryOp(..)
189 | Rvalue::UnaryOp(..)
190 | Rvalue::Discriminant(..)
191 | Rvalue::Aggregate(..)
192 | Rvalue::ShallowInitBox(..) => {
193 // No modification is possible through these r-values.
194 ValueOrPlace::TOP
195 }
196 }
197 }
198
handle_operand( &self, operand: &Operand<'tcx>, state: &mut State<Self::Value>, ) -> ValueOrPlace<Self::Value>199 fn handle_operand(
200 &self,
201 operand: &Operand<'tcx>,
202 state: &mut State<Self::Value>,
203 ) -> ValueOrPlace<Self::Value> {
204 self.super_operand(operand, state)
205 }
206
super_operand( &self, operand: &Operand<'tcx>, state: &mut State<Self::Value>, ) -> ValueOrPlace<Self::Value>207 fn super_operand(
208 &self,
209 operand: &Operand<'tcx>,
210 state: &mut State<Self::Value>,
211 ) -> ValueOrPlace<Self::Value> {
212 match operand {
213 Operand::Constant(box constant) => {
214 ValueOrPlace::Value(self.handle_constant(constant, state))
215 }
216 Operand::Copy(place) | Operand::Move(place) => {
217 // On move, we would ideally flood the place with bottom. But with the current
218 // framework this is not possible (similar to `InterpCx::eval_operand`).
219 self.map()
220 .find(place.as_ref())
221 .map(ValueOrPlace::Place)
222 .unwrap_or(ValueOrPlace::TOP)
223 }
224 }
225 }
226
handle_constant( &self, constant: &Constant<'tcx>, state: &mut State<Self::Value>, ) -> Self::Value227 fn handle_constant(
228 &self,
229 constant: &Constant<'tcx>,
230 state: &mut State<Self::Value>,
231 ) -> Self::Value {
232 self.super_constant(constant, state)
233 }
234
super_constant( &self, _constant: &Constant<'tcx>, _state: &mut State<Self::Value>, ) -> Self::Value235 fn super_constant(
236 &self,
237 _constant: &Constant<'tcx>,
238 _state: &mut State<Self::Value>,
239 ) -> Self::Value {
240 Self::Value::TOP
241 }
242
243 /// The effect of a successful function call return should not be
244 /// applied here, see [`Analysis::apply_terminator_effect`].
handle_terminator(&self, terminator: &Terminator<'tcx>, state: &mut State<Self::Value>)245 fn handle_terminator(&self, terminator: &Terminator<'tcx>, state: &mut State<Self::Value>) {
246 self.super_terminator(terminator, state)
247 }
248
super_terminator(&self, terminator: &Terminator<'tcx>, state: &mut State<Self::Value>)249 fn super_terminator(&self, terminator: &Terminator<'tcx>, state: &mut State<Self::Value>) {
250 match &terminator.kind {
251 TerminatorKind::Call { .. } | TerminatorKind::InlineAsm { .. } => {
252 // Effect is applied by `handle_call_return`.
253 }
254 TerminatorKind::Drop { place, .. } => {
255 state.flood_with(place.as_ref(), self.map(), Self::Value::BOTTOM);
256 }
257 TerminatorKind::Yield { .. } => {
258 // They would have an effect, but are not allowed in this phase.
259 bug!("encountered disallowed terminator");
260 }
261 TerminatorKind::Goto { .. }
262 | TerminatorKind::SwitchInt { .. }
263 | TerminatorKind::Resume
264 | TerminatorKind::Terminate
265 | TerminatorKind::Return
266 | TerminatorKind::Unreachable
267 | TerminatorKind::Assert { .. }
268 | TerminatorKind::GeneratorDrop
269 | TerminatorKind::FalseEdge { .. }
270 | TerminatorKind::FalseUnwind { .. } => {
271 // These terminators have no effect on the analysis.
272 }
273 }
274 }
275
handle_call_return( &self, return_places: CallReturnPlaces<'_, 'tcx>, state: &mut State<Self::Value>, )276 fn handle_call_return(
277 &self,
278 return_places: CallReturnPlaces<'_, 'tcx>,
279 state: &mut State<Self::Value>,
280 ) {
281 self.super_call_return(return_places, state)
282 }
283
super_call_return( &self, return_places: CallReturnPlaces<'_, 'tcx>, state: &mut State<Self::Value>, )284 fn super_call_return(
285 &self,
286 return_places: CallReturnPlaces<'_, 'tcx>,
287 state: &mut State<Self::Value>,
288 ) {
289 return_places.for_each(|place| {
290 state.flood(place.as_ref(), self.map());
291 })
292 }
293
handle_switch_int( &self, discr: &Operand<'tcx>, apply_edge_effects: &mut impl SwitchIntEdgeEffects<State<Self::Value>>, )294 fn handle_switch_int(
295 &self,
296 discr: &Operand<'tcx>,
297 apply_edge_effects: &mut impl SwitchIntEdgeEffects<State<Self::Value>>,
298 ) {
299 self.super_switch_int(discr, apply_edge_effects)
300 }
301
super_switch_int( &self, _discr: &Operand<'tcx>, _apply_edge_effects: &mut impl SwitchIntEdgeEffects<State<Self::Value>>, )302 fn super_switch_int(
303 &self,
304 _discr: &Operand<'tcx>,
305 _apply_edge_effects: &mut impl SwitchIntEdgeEffects<State<Self::Value>>,
306 ) {
307 }
308
wrap(self) -> ValueAnalysisWrapper<Self> where Self: Sized,309 fn wrap(self) -> ValueAnalysisWrapper<Self>
310 where
311 Self: Sized,
312 {
313 ValueAnalysisWrapper(self)
314 }
315 }
316
317 pub struct ValueAnalysisWrapper<T>(pub T);
318
319 impl<'tcx, T: ValueAnalysis<'tcx>> AnalysisDomain<'tcx> for ValueAnalysisWrapper<T> {
320 type Domain = State<T::Value>;
321
322 type Direction = crate::Forward;
323
324 const NAME: &'static str = T::NAME;
325
bottom_value(&self, _body: &Body<'tcx>) -> Self::Domain326 fn bottom_value(&self, _body: &Body<'tcx>) -> Self::Domain {
327 State(StateData::Unreachable)
328 }
329
initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain)330 fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
331 // The initial state maps all tracked places of argument projections to ⊤ and the rest to ⊥.
332 assert!(matches!(state.0, StateData::Unreachable));
333 let values = IndexVec::from_elem_n(T::Value::BOTTOM, self.0.map().value_count);
334 *state = State(StateData::Reachable(values));
335 for arg in body.args_iter() {
336 state.flood(PlaceRef { local: arg, projection: &[] }, self.0.map());
337 }
338 }
339 }
340
341 impl<'tcx, T> Analysis<'tcx> for ValueAnalysisWrapper<T>
342 where
343 T: ValueAnalysis<'tcx>,
344 {
apply_statement_effect( &mut self, state: &mut Self::Domain, statement: &Statement<'tcx>, _location: Location, )345 fn apply_statement_effect(
346 &mut self,
347 state: &mut Self::Domain,
348 statement: &Statement<'tcx>,
349 _location: Location,
350 ) {
351 if state.is_reachable() {
352 self.0.handle_statement(statement, state);
353 }
354 }
355
apply_terminator_effect( &mut self, state: &mut Self::Domain, terminator: &Terminator<'tcx>, _location: Location, )356 fn apply_terminator_effect(
357 &mut self,
358 state: &mut Self::Domain,
359 terminator: &Terminator<'tcx>,
360 _location: Location,
361 ) {
362 if state.is_reachable() {
363 self.0.handle_terminator(terminator, state);
364 }
365 }
366
apply_call_return_effect( &mut self, state: &mut Self::Domain, _block: BasicBlock, return_places: crate::CallReturnPlaces<'_, 'tcx>, )367 fn apply_call_return_effect(
368 &mut self,
369 state: &mut Self::Domain,
370 _block: BasicBlock,
371 return_places: crate::CallReturnPlaces<'_, 'tcx>,
372 ) {
373 if state.is_reachable() {
374 self.0.handle_call_return(return_places, state)
375 }
376 }
377
apply_switch_int_edge_effects( &mut self, _block: BasicBlock, discr: &Operand<'tcx>, apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>, )378 fn apply_switch_int_edge_effects(
379 &mut self,
380 _block: BasicBlock,
381 discr: &Operand<'tcx>,
382 apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
383 ) {
384 // FIXME: Dataflow framework provides no access to current state here.
385 self.0.handle_switch_int(discr, apply_edge_effects)
386 }
387 }
388
389 rustc_index::newtype_index!(
390 /// This index uniquely identifies a place.
391 ///
392 /// Not every place has a `PlaceIndex`, and not every `PlaceIndex` corresponds to a tracked
393 /// place. However, every tracked place and all places along its projection have a `PlaceIndex`.
394 pub struct PlaceIndex {}
395 );
396
397 rustc_index::newtype_index!(
398 /// This index uniquely identifies a tracked place and therefore a slot in [`State`].
399 ///
400 /// It is an implementation detail of this module.
401 struct ValueIndex {}
402 );
403
404 /// See [`State`].
405 #[derive(PartialEq, Eq, Debug)]
406 enum StateData<V> {
407 Reachable(IndexVec<ValueIndex, V>),
408 Unreachable,
409 }
410
411 impl<V: Clone> Clone for StateData<V> {
clone(&self) -> Self412 fn clone(&self) -> Self {
413 match self {
414 Self::Reachable(x) => Self::Reachable(x.clone()),
415 Self::Unreachable => Self::Unreachable,
416 }
417 }
418
clone_from(&mut self, source: &Self)419 fn clone_from(&mut self, source: &Self) {
420 match (&mut *self, source) {
421 (Self::Reachable(x), Self::Reachable(y)) => {
422 // We go through `raw` here, because `IndexVec` currently has a naive `clone_from`.
423 x.raw.clone_from(&y.raw);
424 }
425 _ => *self = source.clone(),
426 }
427 }
428 }
429
430 /// The dataflow state for an instance of [`ValueAnalysis`].
431 ///
432 /// Every instance specifies a lattice that represents the possible values of a single tracked
433 /// place. If we call this lattice `V` and set of tracked places `P`, then a [`State`] is an
434 /// element of `{unreachable} ∪ (P -> V)`. This again forms a lattice, where the bottom element is
435 /// `unreachable` and the top element is the mapping `p ↦ ⊤`. Note that the mapping `p ↦ ⊥` is not
436 /// the bottom element (because joining an unreachable and any other reachable state yields a
437 /// reachable state). All operations on unreachable states are ignored.
438 ///
439 /// Flooding means assigning a value (by default `⊤`) to all tracked projections of a given place.
440 #[derive(PartialEq, Eq, Debug)]
441 pub struct State<V>(StateData<V>);
442
443 impl<V: Clone> Clone for State<V> {
clone(&self) -> Self444 fn clone(&self) -> Self {
445 Self(self.0.clone())
446 }
447
clone_from(&mut self, source: &Self)448 fn clone_from(&mut self, source: &Self) {
449 self.0.clone_from(&source.0);
450 }
451 }
452
453 impl<V: Clone + HasTop + HasBottom> State<V> {
is_reachable(&self) -> bool454 pub fn is_reachable(&self) -> bool {
455 matches!(&self.0, StateData::Reachable(_))
456 }
457
mark_unreachable(&mut self)458 pub fn mark_unreachable(&mut self) {
459 self.0 = StateData::Unreachable;
460 }
461
flood_all(&mut self)462 pub fn flood_all(&mut self) {
463 self.flood_all_with(V::TOP)
464 }
465
flood_all_with(&mut self, value: V)466 pub fn flood_all_with(&mut self, value: V) {
467 let StateData::Reachable(values) = &mut self.0 else { return };
468 values.raw.fill(value);
469 }
470
flood_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V)471 pub fn flood_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) {
472 let StateData::Reachable(values) = &mut self.0 else { return };
473 map.for_each_aliasing_place(place, None, &mut |vi| {
474 values[vi] = value.clone();
475 });
476 }
477
flood(&mut self, place: PlaceRef<'_>, map: &Map)478 pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map) {
479 self.flood_with(place, map, V::TOP)
480 }
481
flood_discr_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V)482 pub fn flood_discr_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) {
483 let StateData::Reachable(values) = &mut self.0 else { return };
484 map.for_each_aliasing_place(place, Some(TrackElem::Discriminant), &mut |vi| {
485 values[vi] = value.clone();
486 });
487 }
488
flood_discr(&mut self, place: PlaceRef<'_>, map: &Map)489 pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map) {
490 self.flood_discr_with(place, map, V::TOP)
491 }
492
493 /// Low-level method that assigns to a place.
494 /// This does nothing if the place is not tracked.
495 ///
496 /// The target place must have been flooded before calling this method.
insert_idx(&mut self, target: PlaceIndex, result: ValueOrPlace<V>, map: &Map)497 pub fn insert_idx(&mut self, target: PlaceIndex, result: ValueOrPlace<V>, map: &Map) {
498 match result {
499 ValueOrPlace::Value(value) => self.insert_value_idx(target, value, map),
500 ValueOrPlace::Place(source) => self.insert_place_idx(target, source, map),
501 }
502 }
503
504 /// Low-level method that assigns a value to a place.
505 /// This does nothing if the place is not tracked.
506 ///
507 /// The target place must have been flooded before calling this method.
insert_value_idx(&mut self, target: PlaceIndex, value: V, map: &Map)508 pub fn insert_value_idx(&mut self, target: PlaceIndex, value: V, map: &Map) {
509 let StateData::Reachable(values) = &mut self.0 else { return };
510 if let Some(value_index) = map.places[target].value_index {
511 values[value_index] = value;
512 }
513 }
514
515 /// Copies `source` to `target`, including all tracked places beneath.
516 ///
517 /// If `target` contains a place that is not contained in `source`, it will be overwritten with
518 /// Top. Also, because this will copy all entries one after another, it may only be used for
519 /// places that are non-overlapping or identical.
520 ///
521 /// The target place must have been flooded before calling this method.
insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map)522 fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) {
523 let StateData::Reachable(values) = &mut self.0 else { return };
524
525 // If both places are tracked, we copy the value to the target.
526 // If the target is tracked, but the source is not, we do nothing, as invalidation has
527 // already been performed.
528 if let Some(target_value) = map.places[target].value_index {
529 if let Some(source_value) = map.places[source].value_index {
530 values[target_value] = values[source_value].clone();
531 }
532 }
533 for target_child in map.children(target) {
534 // Try to find corresponding child and recurse. Reasoning is similar as above.
535 let projection = map.places[target_child].proj_elem.unwrap();
536 if let Some(source_child) = map.projections.get(&(source, projection)) {
537 self.insert_place_idx(target_child, *source_child, map);
538 }
539 }
540 }
541
542 /// Helper method to interpret `target = result`.
assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map)543 pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) {
544 self.flood(target, map);
545 if let Some(target) = map.find(target) {
546 self.insert_idx(target, result, map);
547 }
548 }
549
550 /// Helper method for assignments to a discriminant.
assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map)551 pub fn assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) {
552 self.flood_discr(target, map);
553 if let Some(target) = map.find_discr(target) {
554 self.insert_idx(target, result, map);
555 }
556 }
557
558 /// Retrieve the value stored for a place, or ⊤ if it is not tracked.
get(&self, place: PlaceRef<'_>, map: &Map) -> V559 pub fn get(&self, place: PlaceRef<'_>, map: &Map) -> V {
560 map.find(place).map(|place| self.get_idx(place, map)).unwrap_or(V::TOP)
561 }
562
563 /// Retrieve the value stored for a place, or ⊤ if it is not tracked.
get_discr(&self, place: PlaceRef<'_>, map: &Map) -> V564 pub fn get_discr(&self, place: PlaceRef<'_>, map: &Map) -> V {
565 match map.find_discr(place) {
566 Some(place) => self.get_idx(place, map),
567 None => V::TOP,
568 }
569 }
570
571 /// Retrieve the value stored for a place index, or ⊤ if it is not tracked.
get_idx(&self, place: PlaceIndex, map: &Map) -> V572 pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V {
573 match &self.0 {
574 StateData::Reachable(values) => {
575 map.places[place].value_index.map(|v| values[v].clone()).unwrap_or(V::TOP)
576 }
577 StateData::Unreachable => {
578 // Because this is unreachable, we can return any value we want.
579 V::BOTTOM
580 }
581 }
582 }
583 }
584
585 impl<V: JoinSemiLattice + Clone> JoinSemiLattice for State<V> {
join(&mut self, other: &Self) -> bool586 fn join(&mut self, other: &Self) -> bool {
587 match (&mut self.0, &other.0) {
588 (_, StateData::Unreachable) => false,
589 (StateData::Unreachable, _) => {
590 *self = other.clone();
591 true
592 }
593 (StateData::Reachable(this), StateData::Reachable(other)) => this.join(other),
594 }
595 }
596 }
597
598 /// Partial mapping from [`Place`] to [`PlaceIndex`], where some places also have a [`ValueIndex`].
599 ///
600 /// This data structure essentially maintains a tree of places and their projections. Some
601 /// additional bookkeeping is done, to speed up traversal over this tree:
602 /// - For iteration, every [`PlaceInfo`] contains an intrusive linked list of its children.
603 /// - To directly get the child for a specific projection, there is a `projections` map.
604 #[derive(Debug)]
605 pub struct Map {
606 locals: IndexVec<Local, Option<PlaceIndex>>,
607 projections: FxHashMap<(PlaceIndex, TrackElem), PlaceIndex>,
608 places: IndexVec<PlaceIndex, PlaceInfo>,
609 value_count: usize,
610 // The Range corresponds to a slice into `inner_values_buffer`.
611 inner_values: IndexVec<PlaceIndex, Range<usize>>,
612 inner_values_buffer: Vec<ValueIndex>,
613 }
614
615 impl Map {
new() -> Self616 fn new() -> Self {
617 Self {
618 locals: IndexVec::new(),
619 projections: FxHashMap::default(),
620 places: IndexVec::new(),
621 value_count: 0,
622 inner_values: IndexVec::new(),
623 inner_values_buffer: Vec::new(),
624 }
625 }
626
627 /// Returns a map that only tracks places whose type passes the filter.
628 ///
629 /// This is currently the only way to create a [`Map`]. The way in which the tracked places are
630 /// chosen is an implementation detail and may not be relied upon (other than that their type
631 /// passes the filter).
from_filter<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, filter: impl Fn(Ty<'tcx>) -> bool, value_limit: Option<usize>, ) -> Self632 pub fn from_filter<'tcx>(
633 tcx: TyCtxt<'tcx>,
634 body: &Body<'tcx>,
635 filter: impl Fn(Ty<'tcx>) -> bool,
636 value_limit: Option<usize>,
637 ) -> Self {
638 let mut map = Self::new();
639 let exclude = excluded_locals(body);
640 map.register_with_filter(tcx, body, filter, exclude, value_limit);
641 debug!("registered {} places ({} nodes in total)", map.value_count, map.places.len());
642 map
643 }
644
645 /// Register all non-excluded places that pass the filter.
register_with_filter<'tcx>( &mut self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>, filter: impl Fn(Ty<'tcx>) -> bool, exclude: BitSet<Local>, value_limit: Option<usize>, )646 fn register_with_filter<'tcx>(
647 &mut self,
648 tcx: TyCtxt<'tcx>,
649 body: &Body<'tcx>,
650 filter: impl Fn(Ty<'tcx>) -> bool,
651 exclude: BitSet<Local>,
652 value_limit: Option<usize>,
653 ) {
654 let mut worklist = VecDeque::with_capacity(value_limit.unwrap_or(body.local_decls.len()));
655
656 // Start by constructing the places for each bare local.
657 self.locals = IndexVec::from_elem(None, &body.local_decls);
658 for (local, decl) in body.local_decls.iter_enumerated() {
659 if exclude.contains(local) {
660 continue;
661 }
662
663 // Create a place for the local.
664 debug_assert!(self.locals[local].is_none());
665 let place = self.places.push(PlaceInfo::new(None));
666 self.locals[local] = Some(place);
667
668 // And push the eventual children places to the worklist.
669 self.register_children(tcx, place, decl.ty, &filter, &mut worklist);
670 }
671
672 // `place.elem1.elem2` with type `ty`.
673 // `elem1` is either `Some(Variant(i))` or `None`.
674 while let Some((mut place, elem1, elem2, ty)) = worklist.pop_front() {
675 // The user requires a bound on the number of created values.
676 if let Some(value_limit) = value_limit && self.value_count >= value_limit {
677 break
678 }
679
680 // Create a place for this projection.
681 for elem in [elem1, Some(elem2)].into_iter().flatten() {
682 place = *self.projections.entry((place, elem)).or_insert_with(|| {
683 // Prepend new child to the linked list.
684 let next = self.places.push(PlaceInfo::new(Some(elem)));
685 self.places[next].next_sibling = self.places[place].first_child;
686 self.places[place].first_child = Some(next);
687 next
688 });
689 }
690
691 // And push the eventual children places to the worklist.
692 self.register_children(tcx, place, ty, &filter, &mut worklist);
693 }
694
695 // Pre-compute the tree of ValueIndex nested in each PlaceIndex.
696 // `inner_values_buffer[inner_values[place]]` is the set of all the values
697 // reachable by projecting `place`.
698 self.inner_values_buffer = Vec::with_capacity(self.value_count);
699 self.inner_values = IndexVec::from_elem(0..0, &self.places);
700 for local in body.local_decls.indices() {
701 if let Some(place) = self.locals[local] {
702 self.cache_preorder_invoke(place);
703 }
704 }
705
706 // Trim useless places.
707 for opt_place in self.locals.iter_mut() {
708 if let Some(place) = *opt_place && self.inner_values[place].is_empty() {
709 *opt_place = None;
710 }
711 }
712 #[allow(rustc::potential_query_instability)]
713 self.projections.retain(|_, child| !self.inner_values[*child].is_empty());
714 }
715
716 /// Potentially register the (local, projection) place and its fields, recursively.
717 ///
718 /// Invariant: The projection must only contain trackable elements.
register_children<'tcx>( &mut self, tcx: TyCtxt<'tcx>, place: PlaceIndex, ty: Ty<'tcx>, filter: &impl Fn(Ty<'tcx>) -> bool, worklist: &mut VecDeque<(PlaceIndex, Option<TrackElem>, TrackElem, Ty<'tcx>)>, )719 fn register_children<'tcx>(
720 &mut self,
721 tcx: TyCtxt<'tcx>,
722 place: PlaceIndex,
723 ty: Ty<'tcx>,
724 filter: &impl Fn(Ty<'tcx>) -> bool,
725 worklist: &mut VecDeque<(PlaceIndex, Option<TrackElem>, TrackElem, Ty<'tcx>)>,
726 ) {
727 // Allocate a value slot if it doesn't have one, and the user requested one.
728 if self.places[place].value_index.is_none() && filter(ty) {
729 self.places[place].value_index = Some(self.value_count.into());
730 self.value_count += 1;
731 }
732
733 // For enums, directly create the `Discriminant`, as that's their main use.
734 if ty.is_enum() {
735 let discr_ty = ty.discriminant_ty(tcx);
736 if filter(discr_ty) {
737 let discr = *self
738 .projections
739 .entry((place, TrackElem::Discriminant))
740 .or_insert_with(|| {
741 // Prepend new child to the linked list.
742 let next = self.places.push(PlaceInfo::new(Some(TrackElem::Discriminant)));
743 self.places[next].next_sibling = self.places[place].first_child;
744 self.places[place].first_child = Some(next);
745 next
746 });
747
748 // Allocate a value slot if it doesn't have one.
749 if self.places[discr].value_index.is_none() {
750 self.places[discr].value_index = Some(self.value_count.into());
751 self.value_count += 1;
752 }
753 }
754 }
755
756 // Recurse with all fields of this place.
757 iter_fields(ty, tcx, ty::ParamEnv::reveal_all(), |variant, field, ty| {
758 worklist.push_back((
759 place,
760 variant.map(TrackElem::Variant),
761 TrackElem::Field(field),
762 ty,
763 ))
764 });
765 }
766
767 /// Precompute the list of values inside `root` and store it inside
768 /// as a slice within `inner_values_buffer`.
cache_preorder_invoke(&mut self, root: PlaceIndex)769 fn cache_preorder_invoke(&mut self, root: PlaceIndex) {
770 let start = self.inner_values_buffer.len();
771 if let Some(vi) = self.places[root].value_index {
772 self.inner_values_buffer.push(vi);
773 }
774
775 // We manually iterate instead of using `children` as we need to mutate `self`.
776 let mut next_child = self.places[root].first_child;
777 while let Some(child) = next_child {
778 ensure_sufficient_stack(|| self.cache_preorder_invoke(child));
779 next_child = self.places[child].next_sibling;
780 }
781
782 let end = self.inner_values_buffer.len();
783 self.inner_values[root] = start..end;
784 }
785
786 /// Returns the number of tracked places, i.e., those for which a value can be stored.
tracked_places(&self) -> usize787 pub fn tracked_places(&self) -> usize {
788 self.value_count
789 }
790
791 /// Applies a single projection element, yielding the corresponding child.
apply(&self, place: PlaceIndex, elem: TrackElem) -> Option<PlaceIndex>792 pub fn apply(&self, place: PlaceIndex, elem: TrackElem) -> Option<PlaceIndex> {
793 self.projections.get(&(place, elem)).copied()
794 }
795
796 /// Locates the given place, if it exists in the tree.
find_extra( &self, place: PlaceRef<'_>, extra: impl IntoIterator<Item = TrackElem>, ) -> Option<PlaceIndex>797 pub fn find_extra(
798 &self,
799 place: PlaceRef<'_>,
800 extra: impl IntoIterator<Item = TrackElem>,
801 ) -> Option<PlaceIndex> {
802 let mut index = *self.locals[place.local].as_ref()?;
803
804 for &elem in place.projection {
805 index = self.apply(index, elem.try_into().ok()?)?;
806 }
807 for elem in extra {
808 index = self.apply(index, elem)?;
809 }
810
811 Some(index)
812 }
813
814 /// Locates the given place, if it exists in the tree.
find(&self, place: PlaceRef<'_>) -> Option<PlaceIndex>815 pub fn find(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
816 self.find_extra(place, [])
817 }
818
819 /// Locates the given place and applies `Discriminant`, if it exists in the tree.
find_discr(&self, place: PlaceRef<'_>) -> Option<PlaceIndex>820 pub fn find_discr(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
821 self.find_extra(place, [TrackElem::Discriminant])
822 }
823
824 /// Iterate over all direct children.
children(&self, parent: PlaceIndex) -> impl Iterator<Item = PlaceIndex> + '_825 pub fn children(&self, parent: PlaceIndex) -> impl Iterator<Item = PlaceIndex> + '_ {
826 Children::new(self, parent)
827 }
828
829 /// Invoke a function on the given place and all places that may alias it.
830 ///
831 /// In particular, when the given place has a variant downcast, we invoke the function on all
832 /// the other variants.
833 ///
834 /// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track
835 /// as such.
for_each_aliasing_place( &self, place: PlaceRef<'_>, tail_elem: Option<TrackElem>, f: &mut impl FnMut(ValueIndex), )836 fn for_each_aliasing_place(
837 &self,
838 place: PlaceRef<'_>,
839 tail_elem: Option<TrackElem>,
840 f: &mut impl FnMut(ValueIndex),
841 ) {
842 if place.has_deref() {
843 // We do not track indirect places.
844 return;
845 }
846 let Some(mut index) = self.locals[place.local] else {
847 // The local is not tracked at all, so it does not alias anything.
848 return;
849 };
850 let elems = place
851 .projection
852 .iter()
853 .map(|&elem| elem.try_into())
854 .chain(tail_elem.map(Ok).into_iter());
855 for elem in elems {
856 // A field aliases the parent place.
857 if let Some(vi) = self.places[index].value_index {
858 f(vi);
859 }
860
861 let Ok(elem) = elem else { return };
862 let sub = self.apply(index, elem);
863 if let TrackElem::Variant(..) | TrackElem::Discriminant = elem {
864 // Enum variant fields and enum discriminants alias each another.
865 self.for_each_variant_sibling(index, sub, f);
866 }
867 if let Some(sub) = sub {
868 index = sub
869 } else {
870 return;
871 }
872 }
873 self.for_each_value_inside(index, f);
874 }
875
876 /// Invoke the given function on all the descendants of the given place, except one branch.
for_each_variant_sibling( &self, parent: PlaceIndex, preserved_child: Option<PlaceIndex>, f: &mut impl FnMut(ValueIndex), )877 fn for_each_variant_sibling(
878 &self,
879 parent: PlaceIndex,
880 preserved_child: Option<PlaceIndex>,
881 f: &mut impl FnMut(ValueIndex),
882 ) {
883 for sibling in self.children(parent) {
884 let elem = self.places[sibling].proj_elem;
885 // Only invalidate variants and discriminant. Fields (for generators) are not
886 // invalidated by assignment to a variant.
887 if let Some(TrackElem::Variant(..) | TrackElem::Discriminant) = elem
888 // Only invalidate the other variants, the current one is fine.
889 && Some(sibling) != preserved_child
890 {
891 self.for_each_value_inside(sibling, f);
892 }
893 }
894 }
895
896 /// Invoke a function on each value in the given place and all descendants.
for_each_value_inside(&self, root: PlaceIndex, f: &mut impl FnMut(ValueIndex))897 fn for_each_value_inside(&self, root: PlaceIndex, f: &mut impl FnMut(ValueIndex)) {
898 let range = self.inner_values[root].clone();
899 let values = &self.inner_values_buffer[range];
900 for &v in values {
901 f(v)
902 }
903 }
904 }
905
906 /// This is the information tracked for every [`PlaceIndex`] and is stored by [`Map`].
907 ///
908 /// Together, `first_child` and `next_sibling` form an intrusive linked list, which is used to
909 /// model a tree structure (a replacement for a member like `children: Vec<PlaceIndex>`).
910 #[derive(Debug)]
911 struct PlaceInfo {
912 /// We store a [`ValueIndex`] if and only if the placed is tracked by the analysis.
913 value_index: Option<ValueIndex>,
914
915 /// The projection used to go from parent to this node (only None for root).
916 proj_elem: Option<TrackElem>,
917
918 /// The left-most child.
919 first_child: Option<PlaceIndex>,
920
921 /// Index of the sibling to the right of this node.
922 next_sibling: Option<PlaceIndex>,
923 }
924
925 impl PlaceInfo {
new(proj_elem: Option<TrackElem>) -> Self926 fn new(proj_elem: Option<TrackElem>) -> Self {
927 Self { next_sibling: None, first_child: None, proj_elem, value_index: None }
928 }
929 }
930
931 struct Children<'a> {
932 map: &'a Map,
933 next: Option<PlaceIndex>,
934 }
935
936 impl<'a> Children<'a> {
new(map: &'a Map, parent: PlaceIndex) -> Self937 fn new(map: &'a Map, parent: PlaceIndex) -> Self {
938 Self { map, next: map.places[parent].first_child }
939 }
940 }
941
942 impl<'a> Iterator for Children<'a> {
943 type Item = PlaceIndex;
944
next(&mut self) -> Option<Self::Item>945 fn next(&mut self) -> Option<Self::Item> {
946 match self.next {
947 Some(child) => {
948 self.next = self.map.places[child].next_sibling;
949 Some(child)
950 }
951 None => None,
952 }
953 }
954 }
955
956 /// Used as the result of an operand or r-value.
957 #[derive(Debug)]
958 pub enum ValueOrPlace<V> {
959 Value(V),
960 Place(PlaceIndex),
961 }
962
963 impl<V: HasTop> ValueOrPlace<V> {
964 pub const TOP: Self = ValueOrPlace::Value(V::TOP);
965 }
966
967 /// The set of projection elements that can be used by a tracked place.
968 ///
969 /// Although only field projections are currently allowed, this could change in the future.
970 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
971 pub enum TrackElem {
972 Field(FieldIdx),
973 Variant(VariantIdx),
974 Discriminant,
975 }
976
977 impl<V, T> TryFrom<ProjectionElem<V, T>> for TrackElem {
978 type Error = ();
979
try_from(value: ProjectionElem<V, T>) -> Result<Self, Self::Error>980 fn try_from(value: ProjectionElem<V, T>) -> Result<Self, Self::Error> {
981 match value {
982 ProjectionElem::Field(field, _) => Ok(TrackElem::Field(field)),
983 ProjectionElem::Downcast(_, idx) => Ok(TrackElem::Variant(idx)),
984 _ => Err(()),
985 }
986 }
987 }
988
989 /// Invokes `f` on all direct fields of `ty`.
iter_fields<'tcx>( ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, mut f: impl FnMut(Option<VariantIdx>, FieldIdx, Ty<'tcx>), )990 pub fn iter_fields<'tcx>(
991 ty: Ty<'tcx>,
992 tcx: TyCtxt<'tcx>,
993 param_env: ty::ParamEnv<'tcx>,
994 mut f: impl FnMut(Option<VariantIdx>, FieldIdx, Ty<'tcx>),
995 ) {
996 match ty.kind() {
997 ty::Tuple(list) => {
998 for (field, ty) in list.iter().enumerate() {
999 f(None, field.into(), ty);
1000 }
1001 }
1002 ty::Adt(def, substs) => {
1003 if def.is_union() {
1004 return;
1005 }
1006 for (v_index, v_def) in def.variants().iter_enumerated() {
1007 let variant = if def.is_struct() { None } else { Some(v_index) };
1008 for (f_index, f_def) in v_def.fields.iter().enumerate() {
1009 let field_ty = f_def.ty(tcx, substs);
1010 let field_ty = tcx
1011 .try_normalize_erasing_regions(param_env, field_ty)
1012 .unwrap_or_else(|_| tcx.erase_regions(field_ty));
1013 f(variant, f_index.into(), field_ty);
1014 }
1015 }
1016 }
1017 ty::Closure(_, substs) => {
1018 iter_fields(substs.as_closure().tupled_upvars_ty(), tcx, param_env, f);
1019 }
1020 _ => (),
1021 }
1022 }
1023
1024 /// Returns all locals with projections that have their reference or address taken.
excluded_locals(body: &Body<'_>) -> BitSet<Local>1025 pub fn excluded_locals(body: &Body<'_>) -> BitSet<Local> {
1026 struct Collector {
1027 result: BitSet<Local>,
1028 }
1029
1030 impl<'tcx> Visitor<'tcx> for Collector {
1031 fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
1032 if (context.is_borrow()
1033 || context.is_address_of()
1034 || context.is_drop()
1035 || context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput))
1036 && !place.is_indirect()
1037 {
1038 // A pointer to a place could be used to access other places with the same local,
1039 // hence we have to exclude the local completely.
1040 self.result.insert(place.local);
1041 }
1042 }
1043 }
1044
1045 let mut collector = Collector { result: BitSet::new_empty(body.local_decls.len()) };
1046 collector.visit_body(body);
1047 collector.result
1048 }
1049
1050 /// This is used to visualize the dataflow analysis.
1051 impl<'tcx, T> DebugWithContext<ValueAnalysisWrapper<T>> for State<T::Value>
1052 where
1053 T: ValueAnalysis<'tcx>,
1054 T::Value: Debug,
1055 {
fmt_with(&self, ctxt: &ValueAnalysisWrapper<T>, f: &mut Formatter<'_>) -> std::fmt::Result1056 fn fmt_with(&self, ctxt: &ValueAnalysisWrapper<T>, f: &mut Formatter<'_>) -> std::fmt::Result {
1057 match &self.0 {
1058 StateData::Reachable(values) => debug_with_context(values, None, ctxt.0.map(), f),
1059 StateData::Unreachable => write!(f, "unreachable"),
1060 }
1061 }
1062
fmt_diff_with( &self, old: &Self, ctxt: &ValueAnalysisWrapper<T>, f: &mut Formatter<'_>, ) -> std::fmt::Result1063 fn fmt_diff_with(
1064 &self,
1065 old: &Self,
1066 ctxt: &ValueAnalysisWrapper<T>,
1067 f: &mut Formatter<'_>,
1068 ) -> std::fmt::Result {
1069 match (&self.0, &old.0) {
1070 (StateData::Reachable(this), StateData::Reachable(old)) => {
1071 debug_with_context(this, Some(old), ctxt.0.map(), f)
1072 }
1073 _ => Ok(()), // Consider printing something here.
1074 }
1075 }
1076 }
1077
debug_with_context_rec<V: Debug + Eq>( place: PlaceIndex, place_str: &str, new: &IndexSlice<ValueIndex, V>, old: Option<&IndexSlice<ValueIndex, V>>, map: &Map, f: &mut Formatter<'_>, ) -> std::fmt::Result1078 fn debug_with_context_rec<V: Debug + Eq>(
1079 place: PlaceIndex,
1080 place_str: &str,
1081 new: &IndexSlice<ValueIndex, V>,
1082 old: Option<&IndexSlice<ValueIndex, V>>,
1083 map: &Map,
1084 f: &mut Formatter<'_>,
1085 ) -> std::fmt::Result {
1086 if let Some(value) = map.places[place].value_index {
1087 match old {
1088 None => writeln!(f, "{}: {:?}", place_str, new[value])?,
1089 Some(old) => {
1090 if new[value] != old[value] {
1091 writeln!(f, "\u{001f}-{}: {:?}", place_str, old[value])?;
1092 writeln!(f, "\u{001f}+{}: {:?}", place_str, new[value])?;
1093 }
1094 }
1095 }
1096 }
1097
1098 for child in map.children(place) {
1099 let info_elem = map.places[child].proj_elem.unwrap();
1100 let child_place_str = match info_elem {
1101 TrackElem::Discriminant => {
1102 format!("discriminant({})", place_str)
1103 }
1104 TrackElem::Variant(idx) => {
1105 format!("({} as {:?})", place_str, idx)
1106 }
1107 TrackElem::Field(field) => {
1108 if place_str.starts_with('*') {
1109 format!("({}).{}", place_str, field.index())
1110 } else {
1111 format!("{}.{}", place_str, field.index())
1112 }
1113 }
1114 };
1115 debug_with_context_rec(child, &child_place_str, new, old, map, f)?;
1116 }
1117
1118 Ok(())
1119 }
1120
debug_with_context<V: Debug + Eq>( new: &IndexSlice<ValueIndex, V>, old: Option<&IndexSlice<ValueIndex, V>>, map: &Map, f: &mut Formatter<'_>, ) -> std::fmt::Result1121 fn debug_with_context<V: Debug + Eq>(
1122 new: &IndexSlice<ValueIndex, V>,
1123 old: Option<&IndexSlice<ValueIndex, V>>,
1124 map: &Map,
1125 f: &mut Formatter<'_>,
1126 ) -> std::fmt::Result {
1127 for (local, place) in map.locals.iter_enumerated() {
1128 if let Some(place) = place {
1129 debug_with_context_rec(*place, &format!("{local:?}"), new, old, map, f)?;
1130 }
1131 }
1132 Ok(())
1133 }
1134