1 //! A copy of the `Qualif` trait in `qualify_consts.rs` that is suitable for the new validator.
3 use rustc::hir::def_id::DefId;
5 use rustc::ty::{self, Ty};
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 {
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(
53 Place::ty_from(place.base, proj_base, *cx.body, cx.tcx)
54 .projection_ty(cx.tcx, elem)
59 | ProjectionElem::Subslice { .. }
60 | ProjectionElem::Field(..)
61 | ProjectionElem::ConstantIndex { .. }
62 | ProjectionElem::Downcast(..) => qualif,
64 ProjectionElem::Index(local) => qualif || per_local(*local),
67 bug!("This should be called if projection is not empty");
72 cx: &ConstCx<'_, 'tcx>,
73 per_local: &impl Fn(Local) -> bool,
74 place: PlaceRef<'_, 'tcx>,
76 Self::in_projection_structurally(cx, per_local, place)
80 cx: &ConstCx<'_, 'tcx>,
81 per_local: &impl Fn(Local) -> bool,
82 place: PlaceRef<'_, 'tcx>,
85 PlaceRef { base: PlaceBase::Local(local), projection: [] } => per_local(*local),
86 PlaceRef { base: PlaceBase::Static(_), projection: [] } => {
87 bug!("qualifying already promoted MIR")
89 PlaceRef { base: _, projection: [.., _] } => Self::in_projection(cx, per_local, place),
94 cx: &ConstCx<'_, 'tcx>,
95 per_local: &impl Fn(Local) -> bool,
96 operand: &Operand<'tcx>,
99 Operand::Copy(ref place) | Operand::Move(ref place) => {
100 Self::in_place(cx, per_local, place.as_ref())
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)
111 let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def_id);
112 let qualif = Self::in_qualifs(&qualifs);
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)
126 fn in_rvalue_structurally(
127 cx: &ConstCx<'_, 'tcx>,
128 per_local: &impl Fn(Local) -> bool,
129 rvalue: &Rvalue<'tcx>,
132 Rvalue::NullaryOp(..) => false,
134 Rvalue::Discriminant(ref place) | Rvalue::Len(ref place) => {
135 Self::in_place(cx, per_local, place.as_ref())
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),
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)
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(
156 PlaceRef { base: &place.base, projection: proj_base },
161 Self::in_place(cx, per_local, place.as_ref())
164 Rvalue::Aggregate(_, ref operands) => {
165 operands.iter().any(|o| Self::in_operand(cx, per_local, o))
171 cx: &ConstCx<'_, 'tcx>,
172 per_local: &impl Fn(Local) -> bool,
173 rvalue: &Rvalue<'tcx>,
175 Self::in_rvalue_structurally(cx, per_local, rvalue)
179 cx: &ConstCx<'_, 'tcx>,
180 _per_local: &impl Fn(Local) -> bool,
181 _callee: &Operand<'tcx>,
182 _args: &[Operand<'tcx>],
185 // Be conservative about the returned value of a const fn.
186 Self::in_any_value_of_ty(cx, return_ty)
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;
197 impl Qualif for HasMutInterior {
198 const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";
200 fn in_qualifs(qualifs: &ConstQualifs) -> bool {
201 qualifs.has_mut_interior
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)
209 cx: &ConstCx<'_, 'tcx>,
210 per_local: &impl Fn(Local) -> bool,
211 rvalue: &Rvalue<'tcx>,
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);
227 Self::in_rvalue_structurally(cx, per_local, rvalue)
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;
237 impl Qualif for NeedsDrop {
238 const ANALYSIS_NAME: &'static str = "flow_needs_drop";
239 const IS_CLEARED_ON_MOVE: bool = true;
241 fn in_qualifs(qualifs: &ConstQualifs) -> bool {
245 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
246 ty.needs_drop(cx.tcx, cx.param_env)
250 cx: &ConstCx<'_, 'tcx>,
251 per_local: &impl Fn(Local) -> bool,
252 rvalue: &Rvalue<'tcx>,
254 if let Rvalue::Aggregate(ref kind, _) = *rvalue {
255 if let AggregateKind::Adt(def, ..) = **kind {
256 if def.has_dtor(cx.tcx) {
262 Self::in_rvalue_structurally(cx, per_local, rvalue)