Skip to content

Commit eec93ca

Browse files
Copy Qualif to start work on new const validator
This is an exact copy of the `Qualif` trait from `qualify_consts.rs` and its first two implementers, `HasMutInterior` and `NeedsDrop`.
1 parent 83a3e04 commit eec93ca

File tree

1 file changed

+271
-0
lines changed

1 file changed

+271
-0
lines changed
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
/// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
2+
/// code for promotion or prevent it from evaluating at compile time. So `return true` means
3+
/// "I found something bad, no reason to go on searching". `false` is only returned if we
4+
/// definitely cannot find anything bad anywhere.
5+
///
6+
/// The default implementations proceed structurally.
7+
trait Qualif {
8+
const IDX: usize;
9+
10+
/// Return the qualification that is (conservatively) correct for any value
11+
/// of the type, or `None` if the qualification is not value/type-based.
12+
fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> Option<bool> {
13+
None
14+
}
15+
16+
/// Return a mask for the qualification, given a type. This is `false` iff
17+
/// no value of that type can have the qualification.
18+
fn mask_for_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
19+
Self::in_any_value_of_ty(cx, ty).unwrap_or(true)
20+
}
21+
22+
fn in_local(cx: &ConstCx<'_, '_>, local: Local) -> bool {
23+
cx.per_local.0[Self::IDX].contains(local)
24+
}
25+
26+
fn in_static(_cx: &ConstCx<'_, 'tcx>, _static: &Static<'tcx>) -> bool {
27+
// FIXME(eddyb) should we do anything here for value properties?
28+
false
29+
}
30+
31+
fn in_projection_structurally(
32+
cx: &ConstCx<'_, 'tcx>,
33+
place: PlaceRef<'_, 'tcx>,
34+
) -> bool {
35+
if let [proj_base @ .., elem] = place.projection {
36+
let base_qualif = Self::in_place(cx, PlaceRef {
37+
base: place.base,
38+
projection: proj_base,
39+
});
40+
let qualif = base_qualif && Self::mask_for_ty(
41+
cx,
42+
Place::ty_from(place.base, proj_base, cx.body, cx.tcx)
43+
.projection_ty(cx.tcx, elem)
44+
.ty,
45+
);
46+
match elem {
47+
ProjectionElem::Deref |
48+
ProjectionElem::Subslice { .. } |
49+
ProjectionElem::Field(..) |
50+
ProjectionElem::ConstantIndex { .. } |
51+
ProjectionElem::Downcast(..) => qualif,
52+
53+
ProjectionElem::Index(local) => qualif || Self::in_local(cx, *local),
54+
}
55+
} else {
56+
bug!("This should be called if projection is not empty");
57+
}
58+
}
59+
60+
fn in_projection(
61+
cx: &ConstCx<'_, 'tcx>,
62+
place: PlaceRef<'_, 'tcx>,
63+
) -> bool {
64+
Self::in_projection_structurally(cx, place)
65+
}
66+
67+
fn in_place(cx: &ConstCx<'_, 'tcx>, place: PlaceRef<'_, 'tcx>) -> bool {
68+
match place {
69+
PlaceRef {
70+
base: PlaceBase::Local(local),
71+
projection: [],
72+
} => Self::in_local(cx, *local),
73+
PlaceRef {
74+
base: PlaceBase::Static(box Static {
75+
kind: StaticKind::Promoted(..),
76+
..
77+
}),
78+
projection: [],
79+
} => bug!("qualifying already promoted MIR"),
80+
PlaceRef {
81+
base: PlaceBase::Static(static_),
82+
projection: [],
83+
} => {
84+
Self::in_static(cx, static_)
85+
},
86+
PlaceRef {
87+
base: _,
88+
projection: [.., _],
89+
} => Self::in_projection(cx, place),
90+
}
91+
}
92+
93+
fn in_operand(cx: &ConstCx<'_, 'tcx>, operand: &Operand<'tcx>) -> bool {
94+
match *operand {
95+
Operand::Copy(ref place) |
96+
Operand::Move(ref place) => Self::in_place(cx, place.as_ref()),
97+
98+
Operand::Constant(ref constant) => {
99+
if let ConstValue::Unevaluated(def_id, _) = constant.literal.val {
100+
// Don't peek inside trait associated constants.
101+
if cx.tcx.trait_of_item(def_id).is_some() {
102+
Self::in_any_value_of_ty(cx, constant.literal.ty).unwrap_or(false)
103+
} else {
104+
let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id);
105+
106+
let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX];
107+
108+
// Just in case the type is more specific than
109+
// the definition, e.g., impl associated const
110+
// with type parameters, take it into account.
111+
qualif && Self::mask_for_ty(cx, constant.literal.ty)
112+
}
113+
} else {
114+
false
115+
}
116+
}
117+
}
118+
}
119+
120+
fn in_rvalue_structurally(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
121+
match *rvalue {
122+
Rvalue::NullaryOp(..) => false,
123+
124+
Rvalue::Discriminant(ref place) |
125+
Rvalue::Len(ref place) => Self::in_place(cx, place.as_ref()),
126+
127+
Rvalue::Use(ref operand) |
128+
Rvalue::Repeat(ref operand, _) |
129+
Rvalue::UnaryOp(_, ref operand) |
130+
Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, operand),
131+
132+
Rvalue::BinaryOp(_, ref lhs, ref rhs) |
133+
Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
134+
Self::in_operand(cx, lhs) || Self::in_operand(cx, rhs)
135+
}
136+
137+
Rvalue::Ref(_, _, ref place) => {
138+
// Special-case reborrows to be more like a copy of the reference.
139+
if let box [proj_base @ .., elem] = &place.projection {
140+
if ProjectionElem::Deref == *elem {
141+
let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty;
142+
if let ty::Ref(..) = base_ty.sty {
143+
return Self::in_place(cx, PlaceRef {
144+
base: &place.base,
145+
projection: proj_base,
146+
});
147+
}
148+
}
149+
}
150+
151+
Self::in_place(cx, place.as_ref())
152+
}
153+
154+
Rvalue::Aggregate(_, ref operands) => {
155+
operands.iter().any(|o| Self::in_operand(cx, o))
156+
}
157+
}
158+
}
159+
160+
fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
161+
Self::in_rvalue_structurally(cx, rvalue)
162+
}
163+
164+
fn in_call(
165+
cx: &ConstCx<'_, 'tcx>,
166+
_callee: &Operand<'tcx>,
167+
_args: &[Operand<'tcx>],
168+
return_ty: Ty<'tcx>,
169+
) -> bool {
170+
// Be conservative about the returned value of a const fn.
171+
Self::in_any_value_of_ty(cx, return_ty).unwrap_or(false)
172+
}
173+
174+
fn in_value(cx: &ConstCx<'_, 'tcx>, source: ValueSource<'_, 'tcx>) -> bool {
175+
match source {
176+
ValueSource::Rvalue(rvalue) => Self::in_rvalue(cx, rvalue),
177+
ValueSource::DropAndReplace(source) => Self::in_operand(cx, source),
178+
ValueSource::Call { callee, args, return_ty } => {
179+
Self::in_call(cx, callee, args, return_ty)
180+
}
181+
}
182+
}
183+
}
184+
185+
/// Constant containing interior mutability (`UnsafeCell<T>`).
186+
/// This must be ruled out to make sure that evaluating the constant at compile-time
187+
/// and at *any point* during the run-time would produce the same result. In particular,
188+
/// promotion of temporaries must not change program behavior; if the promoted could be
189+
/// written to, that would be a problem.
190+
struct HasMutInterior;
191+
192+
impl Qualif for HasMutInterior {
193+
const IDX: usize = 0;
194+
195+
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
196+
Some(!ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP))
197+
}
198+
199+
fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
200+
match *rvalue {
201+
// Returning `true` for `Rvalue::Ref` indicates the borrow isn't
202+
// allowed in constants (and the `Checker` will error), and/or it
203+
// won't be promoted, due to `&mut ...` or interior mutability.
204+
Rvalue::Ref(_, kind, ref place) => {
205+
let ty = place.ty(cx.body, cx.tcx).ty;
206+
207+
if let BorrowKind::Mut { .. } = kind {
208+
// In theory, any zero-sized value could be borrowed
209+
// mutably without consequences. However, only &mut []
210+
// is allowed right now, and only in functions.
211+
if cx.mode == Mode::StaticMut {
212+
// Inside a `static mut`, &mut [...] is also allowed.
213+
match ty.sty {
214+
ty::Array(..) | ty::Slice(_) => {}
215+
_ => return true,
216+
}
217+
} else if let ty::Array(_, len) = ty.sty {
218+
// FIXME(eddyb) the `cx.mode == Mode::NonConstFn` condition
219+
// seems unnecessary, given that this is merely a ZST.
220+
match len.try_eval_usize(cx.tcx, cx.param_env) {
221+
Some(0) if cx.mode == Mode::NonConstFn => {},
222+
_ => return true,
223+
}
224+
} else {
225+
return true;
226+
}
227+
}
228+
}
229+
230+
Rvalue::Aggregate(ref kind, _) => {
231+
if let AggregateKind::Adt(def, ..) = **kind {
232+
if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
233+
let ty = rvalue.ty(cx.body, cx.tcx);
234+
assert_eq!(Self::in_any_value_of_ty(cx, ty), Some(true));
235+
return true;
236+
}
237+
}
238+
}
239+
240+
_ => {}
241+
}
242+
243+
Self::in_rvalue_structurally(cx, rvalue)
244+
}
245+
}
246+
247+
/// Constant containing an ADT that implements `Drop`.
248+
/// This must be ruled out (a) because we cannot run `Drop` during compile-time
249+
/// as that might not be a `const fn`, and (b) because implicit promotion would
250+
/// remove side-effects that occur as part of dropping that value.
251+
struct NeedsDrop;
252+
253+
impl Qualif for NeedsDrop {
254+
const IDX: usize = 1;
255+
256+
fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
257+
Some(ty.needs_drop(cx.tcx, cx.param_env))
258+
}
259+
260+
fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
261+
if let Rvalue::Aggregate(ref kind, _) = *rvalue {
262+
if let AggregateKind::Adt(def, ..) = **kind {
263+
if def.has_dtor(cx.tcx) {
264+
return true;
265+
}
266+
}
267+
}
268+
269+
Self::in_rvalue_structurally(cx, rvalue)
270+
}
271+
}

0 commit comments

Comments
 (0)