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