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 syntax_pos::DUMMY_SP;
7 use super::Item as ConstCx;
9 pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs {
11 has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
12 needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
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.
21 /// The default implementations proceed structurally.
23 /// The name of the file used to debug the dataflow analysis that computes this qualif.
24 const ANALYSIS_NAME: &'static str;
26 /// Whether this `Qualif` is cleared when a local is moved from.
27 const IS_CLEARED_ON_MOVE: bool = false;
29 fn in_qualifs(qualifs: &ConstQualifs) -> bool;
31 /// Return the qualification that is (conservatively) correct for any value
33 fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool;
35 fn in_projection_structurally(
36 cx: &ConstCx<'_, 'tcx>,
37 per_local: &impl Fn(Local) -> bool,
38 place: PlaceRef<'_, 'tcx>,
40 if let [proj_base @ .., elem] = place.projection {
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(
46 Place::ty_from(place.base, proj_base, *cx.body, cx.tcx)
47 .projection_ty(cx.tcx, elem)
52 | ProjectionElem::Subslice { .. }
53 | ProjectionElem::Field(..)
54 | ProjectionElem::ConstantIndex { .. }
55 | ProjectionElem::Downcast(..) => qualif,
57 ProjectionElem::Index(local) => qualif || per_local(*local),
60 bug!("This should be called if projection is not empty");
65 cx: &ConstCx<'_, 'tcx>,
66 per_local: &impl Fn(Local) -> bool,
67 place: PlaceRef<'_, 'tcx>,
69 Self::in_projection_structurally(cx, per_local, place)
73 cx: &ConstCx<'_, 'tcx>,
74 per_local: &impl Fn(Local) -> bool,
75 place: PlaceRef<'_, 'tcx>,
78 PlaceRef { base: PlaceBase::Local(local), projection: [] } => per_local(*local),
79 PlaceRef { base: PlaceBase::Static(_), projection: [] } => {
80 bug!("qualifying already promoted MIR")
82 PlaceRef { base: _, projection: [.., _] } => Self::in_projection(cx, per_local, place),
87 cx: &ConstCx<'_, 'tcx>,
88 per_local: &impl Fn(Local) -> bool,
89 operand: &Operand<'tcx>,
92 Operand::Copy(ref place) | Operand::Move(ref place) => {
93 Self::in_place(cx, per_local, place.as_ref())
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.
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)
110 let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def_id);
111 let qualif = Self::in_qualifs(&qualifs);
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)
125 fn in_rvalue_structurally(
126 cx: &ConstCx<'_, 'tcx>,
127 per_local: &impl Fn(Local) -> bool,
128 rvalue: &Rvalue<'tcx>,
131 Rvalue::NullaryOp(..) => false,
133 Rvalue::Discriminant(ref place) | Rvalue::Len(ref place) => {
134 Self::in_place(cx, per_local, place.as_ref())
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),
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)
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(
155 PlaceRef { base: &place.base, projection: proj_base },
160 Self::in_place(cx, per_local, place.as_ref())
163 Rvalue::Aggregate(_, ref operands) => {
164 operands.iter().any(|o| Self::in_operand(cx, per_local, o))
170 cx: &ConstCx<'_, 'tcx>,
171 per_local: &impl Fn(Local) -> bool,
172 rvalue: &Rvalue<'tcx>,
174 Self::in_rvalue_structurally(cx, per_local, rvalue)
178 cx: &ConstCx<'_, 'tcx>,
179 _per_local: &impl Fn(Local) -> bool,
180 _callee: &Operand<'tcx>,
181 _args: &[Operand<'tcx>],
184 // Be conservative about the returned value of a const fn.
185 Self::in_any_value_of_ty(cx, return_ty)
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;
196 impl Qualif for HasMutInterior {
197 const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";
199 fn in_qualifs(qualifs: &ConstQualifs) -> bool {
200 qualifs.has_mut_interior
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)
208 cx: &ConstCx<'_, 'tcx>,
209 per_local: &impl Fn(Local) -> bool,
210 rvalue: &Rvalue<'tcx>,
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);
226 Self::in_rvalue_structurally(cx, per_local, rvalue)
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;
236 impl Qualif for NeedsDrop {
237 const ANALYSIS_NAME: &'static str = "flow_needs_drop";
238 const IS_CLEARED_ON_MOVE: bool = true;
240 fn in_qualifs(qualifs: &ConstQualifs) -> bool {
244 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
245 ty.needs_drop(cx.tcx, cx.param_env)
249 cx: &ConstCx<'_, 'tcx>,
250 per_local: &impl Fn(Local) -> bool,
251 rvalue: &Rvalue<'tcx>,
253 if let Rvalue::Aggregate(ref kind, _) = *rvalue {
254 if let AggregateKind::Adt(def, ..) = **kind {
255 if def.has_dtor(cx.tcx) {
261 Self::in_rvalue_structurally(cx, per_local, rvalue)