]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/check_consts/qualifs.rs
Remove `Rvalue::Ref` handling from `HasMutInterior`
[rust.git] / src / librustc_mir / transform / check_consts / qualifs.rs
1 //! A copy of the `Qualif` trait in `qualify_consts.rs` that is suitable for the new validator.
2
3 use rustc::mir::*;
4 use rustc::ty::{self, Ty};
5 use rustc::hir::def_id::DefId;
6 use syntax_pos::DUMMY_SP;
7
8 use super::Item as ConstCx;
9
10 pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs {
11     ConstQualifs {
12         has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
13         needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
14     }
15 }
16
17 /// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
18 /// code for promotion or prevent it from evaluating at compile time. So `return true` means
19 /// "I found something bad, no reason to go on searching". `false` is only returned if we
20 /// definitely cannot find anything bad anywhere.
21 ///
22 /// The default implementations proceed structurally.
23 pub trait Qualif {
24     /// The name of the file used to debug the dataflow analysis that computes this qualif.
25     const ANALYSIS_NAME: &'static str;
26
27     /// Whether this `Qualif` is cleared when a local is moved from.
28     const IS_CLEARED_ON_MOVE: bool = false;
29
30     fn in_qualifs(qualifs: &ConstQualifs) -> bool;
31
32     /// Return the qualification that is (conservatively) correct for any value
33     /// of the type.
34     fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool;
35
36     fn in_static(cx: &ConstCx<'_, 'tcx>, def_id: DefId) -> bool {
37         // `mir_const_qualif` does return the qualifs in the final value of a `static`, so we could
38         // use value-based qualification here, but we shouldn't do this without a good reason.
39         Self::in_any_value_of_ty(cx, cx.tcx.type_of(def_id))
40     }
41
42     fn in_projection_structurally(
43         cx: &ConstCx<'_, 'tcx>,
44         per_local: &impl Fn(Local) -> bool,
45         place: PlaceRef<'_, 'tcx>,
46     ) -> bool {
47         if let [proj_base @ .., elem] = place.projection {
48             let base_qualif = Self::in_place(cx, per_local, PlaceRef {
49                 base: place.base,
50                 projection: proj_base,
51             });
52             let qualif = base_qualif && Self::in_any_value_of_ty(
53                 cx,
54                 Place::ty_from(place.base, proj_base, cx.body, cx.tcx)
55                     .projection_ty(cx.tcx, elem)
56                     .ty,
57             );
58             match elem {
59                 ProjectionElem::Deref |
60                 ProjectionElem::Subslice { .. } |
61                 ProjectionElem::Field(..) |
62                 ProjectionElem::ConstantIndex { .. } |
63                 ProjectionElem::Downcast(..) => qualif,
64
65                 ProjectionElem::Index(local) => qualif || per_local(*local),
66             }
67         } else {
68             bug!("This should be called if projection is not empty");
69         }
70     }
71
72     fn in_projection(
73         cx: &ConstCx<'_, 'tcx>,
74         per_local: &impl Fn(Local) -> bool,
75         place: PlaceRef<'_, 'tcx>,
76     ) -> bool {
77         Self::in_projection_structurally(cx, per_local, place)
78     }
79
80     fn in_place(
81         cx: &ConstCx<'_, 'tcx>,
82         per_local: &impl Fn(Local) -> bool,
83         place: PlaceRef<'_, 'tcx>,
84     ) -> bool {
85         match place {
86             PlaceRef {
87                 base: PlaceBase::Local(local),
88                 projection: [],
89             } => per_local(*local),
90             PlaceRef {
91                 base: PlaceBase::Static(_),
92                 projection: [],
93             } => bug!("qualifying already promoted MIR"),
94             PlaceRef {
95                 base: _,
96                 projection: [.., _],
97             } => Self::in_projection(cx, per_local, place),
98         }
99     }
100
101     fn in_operand(
102         cx: &ConstCx<'_, 'tcx>,
103         per_local: &impl Fn(Local) -> bool,
104         operand: &Operand<'tcx>,
105     ) -> bool {
106         match *operand {
107             Operand::Copy(ref place) |
108             Operand::Move(ref place) => Self::in_place(cx, per_local, place.as_ref()),
109
110             Operand::Constant(ref constant) => {
111                 if let Some(static_) = constant.check_static_ptr(cx.tcx) {
112                     Self::in_static(cx, static_)
113                 } else if let ty::ConstKind::Unevaluated(def_id, _) = constant.literal.val {
114                     // Don't peek inside trait associated constants.
115                     if cx.tcx.trait_of_item(def_id).is_some() {
116                         Self::in_any_value_of_ty(cx, constant.literal.ty)
117                     } else {
118                         let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def_id);
119                         let qualif = Self::in_qualifs(&qualifs);
120
121                         // Just in case the type is more specific than
122                         // the definition, e.g., impl associated const
123                         // with type parameters, take it into account.
124                         qualif && Self::in_any_value_of_ty(cx, constant.literal.ty)
125                     }
126                 } else {
127                     false
128                 }
129             }
130         }
131     }
132
133     fn in_rvalue_structurally(
134         cx: &ConstCx<'_, 'tcx>,
135         per_local: &impl Fn(Local) -> bool,
136         rvalue: &Rvalue<'tcx>,
137     ) -> bool {
138         match *rvalue {
139             Rvalue::NullaryOp(..) => false,
140
141             Rvalue::Discriminant(ref place) |
142             Rvalue::Len(ref place) => Self::in_place(cx, per_local, place.as_ref()),
143
144             Rvalue::Use(ref operand) |
145             Rvalue::Repeat(ref operand, _) |
146             Rvalue::UnaryOp(_, ref operand) |
147             Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, per_local, operand),
148
149             Rvalue::BinaryOp(_, ref lhs, ref rhs) |
150             Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
151                 Self::in_operand(cx, per_local, lhs) || Self::in_operand(cx, per_local, rhs)
152             }
153
154             Rvalue::Ref(_, _, ref place) => {
155                 // Special-case reborrows to be more like a copy of the reference.
156                 if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
157                     if ProjectionElem::Deref == elem {
158                         let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty;
159                         if let ty::Ref(..) = base_ty.kind {
160                             return Self::in_place(cx, per_local, PlaceRef {
161                                 base: &place.base,
162                                 projection: proj_base,
163                             });
164                         }
165                     }
166                 }
167
168                 Self::in_place(cx, per_local, place.as_ref())
169             }
170
171             Rvalue::Aggregate(_, ref operands) => {
172                 operands.iter().any(|o| Self::in_operand(cx, per_local, o))
173             }
174         }
175     }
176
177     fn in_rvalue(
178         cx: &ConstCx<'_, 'tcx>,
179         per_local: &impl Fn(Local) -> bool,
180         rvalue: &Rvalue<'tcx>,
181     ) -> bool {
182         Self::in_rvalue_structurally(cx, per_local, rvalue)
183     }
184
185     fn in_call(
186         cx: &ConstCx<'_, 'tcx>,
187         _per_local: &impl Fn(Local) -> bool,
188         _callee: &Operand<'tcx>,
189         _args: &[Operand<'tcx>],
190         return_ty: Ty<'tcx>,
191     ) -> bool {
192         // Be conservative about the returned value of a const fn.
193         Self::in_any_value_of_ty(cx, return_ty)
194     }
195 }
196
197 /// Constant containing interior mutability (`UnsafeCell<T>`).
198 /// This must be ruled out to make sure that evaluating the constant at compile-time
199 /// and at *any point* during the run-time would produce the same result. In particular,
200 /// promotion of temporaries must not change program behavior; if the promoted could be
201 /// written to, that would be a problem.
202 pub struct HasMutInterior;
203
204 impl Qualif for HasMutInterior {
205     const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";
206
207     fn in_qualifs(qualifs: &ConstQualifs) -> bool {
208         qualifs.has_mut_interior
209     }
210
211     fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
212         !ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP)
213     }
214
215     fn in_rvalue(
216         cx: &ConstCx<'_, 'tcx>,
217         per_local: &impl Fn(Local) -> bool,
218         rvalue: &Rvalue<'tcx>,
219     ) -> bool {
220         match *rvalue {
221             Rvalue::Aggregate(ref kind, _) => {
222                 if let AggregateKind::Adt(def, ..) = **kind {
223                     if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
224                         let ty = rvalue.ty(cx.body, cx.tcx);
225                         assert_eq!(Self::in_any_value_of_ty(cx, ty), true);
226                         return true;
227                     }
228                 }
229             }
230
231             _ => {}
232         }
233
234         Self::in_rvalue_structurally(cx, per_local, rvalue)
235     }
236 }
237
238 /// Constant containing an ADT that implements `Drop`.
239 /// This must be ruled out (a) because we cannot run `Drop` during compile-time
240 /// as that might not be a `const fn`, and (b) because implicit promotion would
241 /// remove side-effects that occur as part of dropping that value.
242 pub struct NeedsDrop;
243
244 impl Qualif for NeedsDrop {
245     const ANALYSIS_NAME: &'static str = "flow_needs_drop";
246     const IS_CLEARED_ON_MOVE: bool = true;
247
248     fn in_qualifs(qualifs: &ConstQualifs) -> bool {
249         qualifs.needs_drop
250     }
251
252     fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
253         ty.needs_drop(cx.tcx, cx.param_env)
254     }
255
256     fn in_rvalue(
257         cx: &ConstCx<'_, 'tcx>,
258         per_local: &impl Fn(Local) -> bool,
259         rvalue: &Rvalue<'tcx>,
260     ) -> bool {
261         if let Rvalue::Aggregate(ref kind, _) = *rvalue {
262             if let AggregateKind::Adt(def, ..) = **kind {
263                 if def.has_dtor(cx.tcx) {
264                     return true;
265                 }
266             }
267         }
268
269         Self::in_rvalue_structurally(cx, per_local, rvalue)
270     }
271 }