]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/check_consts/qualifs.rs
Rollup merge of #67462 - DutchGhost:const_slice_from_raw_parts, r=dtolnay
[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) | Rvalue::AddressOf(_, ref place) => {
155                 // Special-case reborrows to be more like a copy of the reference.
156                 if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() {
157                     let base_ty = Place::ty_from(&place.base, proj_base, *cx.body, cx.tcx).ty;
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                 Self::in_place(cx, per_local, place.as_ref())
167             }
168
169             Rvalue::Aggregate(_, ref operands) => {
170                 operands.iter().any(|o| Self::in_operand(cx, per_local, o))
171             }
172         }
173     }
174
175     fn in_rvalue(
176         cx: &ConstCx<'_, 'tcx>,
177         per_local: &impl Fn(Local) -> bool,
178         rvalue: &Rvalue<'tcx>,
179     ) -> bool {
180         Self::in_rvalue_structurally(cx, per_local, rvalue)
181     }
182
183     fn in_call(
184         cx: &ConstCx<'_, 'tcx>,
185         _per_local: &impl Fn(Local) -> bool,
186         _callee: &Operand<'tcx>,
187         _args: &[Operand<'tcx>],
188         return_ty: Ty<'tcx>,
189     ) -> bool {
190         // Be conservative about the returned value of a const fn.
191         Self::in_any_value_of_ty(cx, return_ty)
192     }
193 }
194
195 /// Constant containing interior mutability (`UnsafeCell<T>`).
196 /// This must be ruled out to make sure that evaluating the constant at compile-time
197 /// and at *any point* during the run-time would produce the same result. In particular,
198 /// promotion of temporaries must not change program behavior; if the promoted could be
199 /// written to, that would be a problem.
200 pub struct HasMutInterior;
201
202 impl Qualif for HasMutInterior {
203     const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";
204
205     fn in_qualifs(qualifs: &ConstQualifs) -> bool {
206         qualifs.has_mut_interior
207     }
208
209     fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
210         !ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP)
211     }
212
213     fn in_rvalue(
214         cx: &ConstCx<'_, 'tcx>,
215         per_local: &impl Fn(Local) -> bool,
216         rvalue: &Rvalue<'tcx>,
217     ) -> bool {
218         match *rvalue {
219             Rvalue::Aggregate(ref kind, _) => {
220                 if let AggregateKind::Adt(def, ..) = **kind {
221                     if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
222                         let ty = rvalue.ty(*cx.body, cx.tcx);
223                         assert_eq!(Self::in_any_value_of_ty(cx, ty), true);
224                         return true;
225                     }
226                 }
227             }
228
229             _ => {}
230         }
231
232         Self::in_rvalue_structurally(cx, per_local, rvalue)
233     }
234 }
235
236 /// Constant containing an ADT that implements `Drop`.
237 /// This must be ruled out (a) because we cannot run `Drop` during compile-time
238 /// as that might not be a `const fn`, and (b) because implicit promotion would
239 /// remove side-effects that occur as part of dropping that value.
240 pub struct NeedsDrop;
241
242 impl Qualif for NeedsDrop {
243     const ANALYSIS_NAME: &'static str = "flow_needs_drop";
244     const IS_CLEARED_ON_MOVE: bool = true;
245
246     fn in_qualifs(qualifs: &ConstQualifs) -> bool {
247         qualifs.needs_drop
248     }
249
250     fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
251         ty.needs_drop(cx.tcx, cx.param_env)
252     }
253
254     fn in_rvalue(
255         cx: &ConstCx<'_, 'tcx>,
256         per_local: &impl Fn(Local) -> bool,
257         rvalue: &Rvalue<'tcx>,
258     ) -> bool {
259         if let Rvalue::Aggregate(ref kind, _) = *rvalue {
260             if let AggregateKind::Adt(def, ..) = **kind {
261                 if def.has_dtor(cx.tcx) {
262                     return true;
263                 }
264             }
265         }
266
267         Self::in_rvalue_structurally(cx, per_local, rvalue)
268     }
269 }