]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/transform/check_consts/qualifs.rs
New upstream version 1.41.1+dfsg1
[rustc.git] / src / librustc_mir / transform / check_consts / qualifs.rs
CommitLineData
e74abb32
XL
1//! A copy of the `Qualif` trait in `qualify_consts.rs` that is suitable for the new validator.
2
3use rustc::mir::*;
e74abb32
XL
4use rustc::ty::{self, Ty};
5use syntax_pos::DUMMY_SP;
6
60c5eb7d 7use super::Item as ConstCx;
e74abb32 8
60c5eb7d
XL
9pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs {
10 ConstQualifs {
11 has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
12 needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
e74abb32
XL
13 }
14}
15
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.
20///
21/// The default implementations proceed structurally.
22pub trait Qualif {
e74abb32
XL
23 /// The name of the file used to debug the dataflow analysis that computes this qualif.
24 const ANALYSIS_NAME: &'static str;
25
26 /// Whether this `Qualif` is cleared when a local is moved from.
27 const IS_CLEARED_ON_MOVE: bool = false;
28
60c5eb7d
XL
29 fn in_qualifs(qualifs: &ConstQualifs) -> bool;
30
e74abb32
XL
31 /// Return the qualification that is (conservatively) correct for any value
32 /// of the type.
33 fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool;
34
e74abb32
XL
35 fn in_projection_structurally(
36 cx: &ConstCx<'_, 'tcx>,
37 per_local: &impl Fn(Local) -> bool,
38 place: PlaceRef<'_, 'tcx>,
39 ) -> bool {
40 if let [proj_base @ .., elem] = place.projection {
41 let base_qualif = Self::in_place(cx, per_local, PlaceRef {
42 base: place.base,
43 projection: proj_base,
44 });
45 let qualif = base_qualif && Self::in_any_value_of_ty(
46 cx,
60c5eb7d 47 Place::ty_from(place.base, proj_base, *cx.body, cx.tcx)
e74abb32
XL
48 .projection_ty(cx.tcx, elem)
49 .ty,
50 );
51 match elem {
52 ProjectionElem::Deref |
53 ProjectionElem::Subslice { .. } |
54 ProjectionElem::Field(..) |
55 ProjectionElem::ConstantIndex { .. } |
56 ProjectionElem::Downcast(..) => qualif,
57
58 ProjectionElem::Index(local) => qualif || per_local(*local),
59 }
60 } else {
61 bug!("This should be called if projection is not empty");
62 }
63 }
64
65 fn in_projection(
66 cx: &ConstCx<'_, 'tcx>,
67 per_local: &impl Fn(Local) -> bool,
68 place: PlaceRef<'_, 'tcx>,
69 ) -> bool {
70 Self::in_projection_structurally(cx, per_local, place)
71 }
72
73 fn in_place(
74 cx: &ConstCx<'_, 'tcx>,
75 per_local: &impl Fn(Local) -> bool,
76 place: PlaceRef<'_, 'tcx>,
77 ) -> bool {
78 match place {
79 PlaceRef {
80 base: PlaceBase::Local(local),
81 projection: [],
82 } => per_local(*local),
83 PlaceRef {
60c5eb7d 84 base: PlaceBase::Static(_),
e74abb32
XL
85 projection: [],
86 } => bug!("qualifying already promoted MIR"),
e74abb32
XL
87 PlaceRef {
88 base: _,
89 projection: [.., _],
90 } => Self::in_projection(cx, per_local, place),
91 }
92 }
93
94 fn in_operand(
95 cx: &ConstCx<'_, 'tcx>,
96 per_local: &impl Fn(Local) -> bool,
97 operand: &Operand<'tcx>,
98 ) -> bool {
99 match *operand {
100 Operand::Copy(ref place) |
101 Operand::Move(ref place) => Self::in_place(cx, per_local, place.as_ref()),
102
103 Operand::Constant(ref constant) => {
60c5eb7d
XL
104 if constant.check_static_ptr(cx.tcx).is_some() {
105 // `mir_const_qualif` does return the qualifs in the final value of a `static`,
106 // so we could use value-based qualification here, but we shouldn't do this
107 // without a good reason.
108 //
109 // Note: this uses `constant.literal.ty` which is a reference or pointer to the
110 // type of the actual `static` item.
111 Self::in_any_value_of_ty(cx, constant.literal.ty)
112 } else if let ty::ConstKind::Unevaluated(def_id, _) = constant.literal.val {
e74abb32
XL
113 // Don't peek inside trait associated constants.
114 if cx.tcx.trait_of_item(def_id).is_some() {
115 Self::in_any_value_of_ty(cx, constant.literal.ty)
116 } else {
60c5eb7d
XL
117 let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def_id);
118 let qualif = Self::in_qualifs(&qualifs);
e74abb32
XL
119
120 // Just in case the type is more specific than
121 // the definition, e.g., impl associated const
122 // with type parameters, take it into account.
123 qualif && Self::in_any_value_of_ty(cx, constant.literal.ty)
124 }
125 } else {
126 false
127 }
128 }
129 }
130 }
131
132 fn in_rvalue_structurally(
133 cx: &ConstCx<'_, 'tcx>,
134 per_local: &impl Fn(Local) -> bool,
135 rvalue: &Rvalue<'tcx>,
136 ) -> bool {
137 match *rvalue {
138 Rvalue::NullaryOp(..) => false,
139
140 Rvalue::Discriminant(ref place) |
141 Rvalue::Len(ref place) => Self::in_place(cx, per_local, place.as_ref()),
142
143 Rvalue::Use(ref operand) |
144 Rvalue::Repeat(ref operand, _) |
145 Rvalue::UnaryOp(_, ref operand) |
146 Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, per_local, operand),
147
148 Rvalue::BinaryOp(_, ref lhs, ref rhs) |
149 Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
150 Self::in_operand(cx, per_local, lhs) || Self::in_operand(cx, per_local, rhs)
151 }
152
153 Rvalue::Ref(_, _, ref place) => {
154 // Special-case reborrows to be more like a copy of the reference.
155 if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
156 if ProjectionElem::Deref == elem {
60c5eb7d 157 let base_ty = Place::ty_from(&place.base, proj_base, *cx.body, cx.tcx).ty;
e74abb32
XL
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
167 Self::in_place(cx, per_local, place.as_ref())
168 }
169
170 Rvalue::Aggregate(_, ref operands) => {
171 operands.iter().any(|o| Self::in_operand(cx, per_local, o))
172 }
173 }
174 }
175
176 fn in_rvalue(
177 cx: &ConstCx<'_, 'tcx>,
178 per_local: &impl Fn(Local) -> bool,
179 rvalue: &Rvalue<'tcx>,
180 ) -> bool {
181 Self::in_rvalue_structurally(cx, per_local, rvalue)
182 }
183
184 fn in_call(
185 cx: &ConstCx<'_, 'tcx>,
186 _per_local: &impl Fn(Local) -> bool,
187 _callee: &Operand<'tcx>,
188 _args: &[Operand<'tcx>],
189 return_ty: Ty<'tcx>,
190 ) -> bool {
191 // Be conservative about the returned value of a const fn.
192 Self::in_any_value_of_ty(cx, return_ty)
193 }
194}
195
196/// Constant containing interior mutability (`UnsafeCell<T>`).
197/// This must be ruled out to make sure that evaluating the constant at compile-time
198/// and at *any point* during the run-time would produce the same result. In particular,
199/// promotion of temporaries must not change program behavior; if the promoted could be
200/// written to, that would be a problem.
201pub struct HasMutInterior;
202
203impl Qualif for HasMutInterior {
e74abb32
XL
204 const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";
205
60c5eb7d
XL
206 fn in_qualifs(qualifs: &ConstQualifs) -> bool {
207 qualifs.has_mut_interior
208 }
209
e74abb32
XL
210 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
211 !ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP)
212 }
213
214 fn in_rvalue(
215 cx: &ConstCx<'_, 'tcx>,
216 per_local: &impl Fn(Local) -> bool,
217 rvalue: &Rvalue<'tcx>,
218 ) -> bool {
219 match *rvalue {
e74abb32
XL
220 Rvalue::Aggregate(ref kind, _) => {
221 if let AggregateKind::Adt(def, ..) = **kind {
222 if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
60c5eb7d 223 let ty = rvalue.ty(*cx.body, cx.tcx);
e74abb32
XL
224 assert_eq!(Self::in_any_value_of_ty(cx, ty), true);
225 return true;
226 }
227 }
228 }
229
230 _ => {}
231 }
232
233 Self::in_rvalue_structurally(cx, per_local, rvalue)
234 }
235}
236
237/// Constant containing an ADT that implements `Drop`.
238/// This must be ruled out (a) because we cannot run `Drop` during compile-time
239/// as that might not be a `const fn`, and (b) because implicit promotion would
240/// remove side-effects that occur as part of dropping that value.
241pub struct NeedsDrop;
242
243impl Qualif for NeedsDrop {
e74abb32
XL
244 const ANALYSIS_NAME: &'static str = "flow_needs_drop";
245 const IS_CLEARED_ON_MOVE: bool = true;
246
60c5eb7d
XL
247 fn in_qualifs(qualifs: &ConstQualifs) -> bool {
248 qualifs.needs_drop
249 }
250
e74abb32
XL
251 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
252 ty.needs_drop(cx.tcx, cx.param_env)
253 }
254
255 fn in_rvalue(
256 cx: &ConstCx<'_, 'tcx>,
257 per_local: &impl Fn(Local) -> bool,
258 rvalue: &Rvalue<'tcx>,
259 ) -> bool {
260 if let Rvalue::Aggregate(ref kind, _) = *rvalue {
261 if let AggregateKind::Adt(def, ..) = **kind {
262 if def.has_dtor(cx.tcx) {
263 return true;
264 }
265 }
266 }
267
268 Self::in_rvalue_structurally(cx, per_local, rvalue)
269 }
270}