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