1 //! A copy of the `Qualif` trait in `qualify_consts.rs` that is suitable for the new validator.
4 use rustc::ty::{self, Ty};
5 use rustc::hir::def_id::DefId;
6 use syntax_pos::DUMMY_SP;
8 use super::Item as ConstCx;
10 pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs {
12 has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
13 needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
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.
22 /// The default implementations proceed structurally.
24 /// The name of the file used to debug the dataflow analysis that computes this qualif.
25 const ANALYSIS_NAME: &'static str;
27 /// Whether this `Qualif` is cleared when a local is moved from.
28 const IS_CLEARED_ON_MOVE: bool = false;
30 fn in_qualifs(qualifs: &ConstQualifs) -> bool;
32 /// Return the qualification that is (conservatively) correct for any value
34 fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool;
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))
42 fn in_projection_structurally(
43 cx: &ConstCx<'_, 'tcx>,
44 per_local: &impl Fn(Local) -> bool,
45 place: PlaceRef<'_, 'tcx>,
47 if let [proj_base @ .., elem] = place.projection {
48 let base_qualif = Self::in_place(cx, per_local, PlaceRef {
50 projection: proj_base,
52 let qualif = base_qualif && Self::in_any_value_of_ty(
54 Place::ty_from(place.base, proj_base, cx.body, cx.tcx)
55 .projection_ty(cx.tcx, elem)
59 ProjectionElem::Deref |
60 ProjectionElem::Subslice { .. } |
61 ProjectionElem::Field(..) |
62 ProjectionElem::ConstantIndex { .. } |
63 ProjectionElem::Downcast(..) => qualif,
65 ProjectionElem::Index(local) => qualif || per_local(*local),
68 bug!("This should be called if projection is not empty");
73 cx: &ConstCx<'_, 'tcx>,
74 per_local: &impl Fn(Local) -> bool,
75 place: PlaceRef<'_, 'tcx>,
77 Self::in_projection_structurally(cx, per_local, place)
81 cx: &ConstCx<'_, 'tcx>,
82 per_local: &impl Fn(Local) -> bool,
83 place: PlaceRef<'_, 'tcx>,
87 base: PlaceBase::Local(local),
89 } => per_local(*local),
91 base: PlaceBase::Static(_),
93 } => bug!("qualifying already promoted MIR"),
97 } => Self::in_projection(cx, per_local, place),
102 cx: &ConstCx<'_, 'tcx>,
103 per_local: &impl Fn(Local) -> bool,
104 operand: &Operand<'tcx>,
107 Operand::Copy(ref place) |
108 Operand::Move(ref place) => Self::in_place(cx, per_local, place.as_ref()),
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)
118 let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def_id);
119 let qualif = Self::in_qualifs(&qualifs);
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)
133 fn in_rvalue_structurally(
134 cx: &ConstCx<'_, 'tcx>,
135 per_local: &impl Fn(Local) -> bool,
136 rvalue: &Rvalue<'tcx>,
139 Rvalue::NullaryOp(..) => false,
141 Rvalue::Discriminant(ref place) |
142 Rvalue::Len(ref place) => Self::in_place(cx, per_local, place.as_ref()),
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),
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)
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 {
162 projection: proj_base,
168 Self::in_place(cx, per_local, place.as_ref())
171 Rvalue::Aggregate(_, ref operands) => {
172 operands.iter().any(|o| Self::in_operand(cx, per_local, o))
178 cx: &ConstCx<'_, 'tcx>,
179 per_local: &impl Fn(Local) -> bool,
180 rvalue: &Rvalue<'tcx>,
182 Self::in_rvalue_structurally(cx, per_local, rvalue)
186 cx: &ConstCx<'_, 'tcx>,
187 _per_local: &impl Fn(Local) -> bool,
188 _callee: &Operand<'tcx>,
189 _args: &[Operand<'tcx>],
192 // Be conservative about the returned value of a const fn.
193 Self::in_any_value_of_ty(cx, return_ty)
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;
204 impl Qualif for HasMutInterior {
205 const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";
207 fn in_qualifs(qualifs: &ConstQualifs) -> bool {
208 qualifs.has_mut_interior
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)
216 cx: &ConstCx<'_, 'tcx>,
217 per_local: &impl Fn(Local) -> bool,
218 rvalue: &Rvalue<'tcx>,
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);
234 Self::in_rvalue_structurally(cx, per_local, rvalue)
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;
244 impl Qualif for NeedsDrop {
245 const ANALYSIS_NAME: &'static str = "flow_needs_drop";
246 const IS_CLEARED_ON_MOVE: bool = true;
248 fn in_qualifs(qualifs: &ConstQualifs) -> bool {
252 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
253 ty.needs_drop(cx.tcx, cx.param_env)
257 cx: &ConstCx<'_, 'tcx>,
258 per_local: &impl Fn(Local) -> bool,
259 rvalue: &Rvalue<'tcx>,
261 if let Rvalue::Aggregate(ref kind, _) = *rvalue {
262 if let AggregateKind::Adt(def, ..) = **kind {
263 if def.has_dtor(cx.tcx) {
269 Self::in_rvalue_structurally(cx, per_local, rvalue)