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