Skip to content

Commit 5aaf72f

Browse files
committed
replace some mode comparisons by more readable function call, rename some Mode, and more comments
1 parent 00f8f82 commit 5aaf72f

File tree

1 file changed

+72
-46
lines changed

1 file changed

+72
-46
lines changed

src/librustc_mir/transform/qualify_consts.rs

+72-46
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,26 @@ use super::promote_consts::{self, Candidate, TempState};
3535
/// What kind of item we are in.
3636
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
3737
enum Mode {
38-
Const,
38+
/// A `static` item.
3939
Static,
40+
/// A `static mut` item.
4041
StaticMut,
42+
/// A `const fn` item.
4143
ConstFn,
42-
Fn
44+
/// A `const` item or an anonymous constant (e.g. in array lengths).
45+
Const,
46+
/// Other type of `fn`.
47+
NonConstFn,
48+
}
49+
50+
impl Mode {
51+
/// Determine whether we are running in "const context". "const context" refers
52+
/// to code type-checked according to the rules of the "const type system":
53+
/// the bodies of const/static items and `const fn`.
54+
#[inline]
55+
fn requires_const_checking(self) -> bool {
56+
self != Mode::NonConstFn
57+
}
4358
}
4459

4560
impl fmt::Display for Mode {
@@ -48,7 +63,7 @@ impl fmt::Display for Mode {
4863
Mode::Const => write!(f, "constant"),
4964
Mode::Static | Mode::StaticMut => write!(f, "static"),
5065
Mode::ConstFn => write!(f, "constant function"),
51-
Mode::Fn => write!(f, "function")
66+
Mode::NonConstFn => write!(f, "function")
5267
}
5368
}
5469
}
@@ -135,10 +150,10 @@ enum ValueSource<'a, 'tcx> {
135150
},
136151
}
137152

138-
/// A "qualif" is a way to lookg for something "bad" in the MIR that would prevent
139-
/// proper const evaluation. So `return true` means "I found something bad, no reason
140-
/// to go on searching". `false` is only returned if we definitely cannot find anything
141-
/// bad anywhere.
153+
/// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
154+
/// code for promotion or prevent it from evaluating at compile time. So `return true` means
155+
/// "I found something bad, no reason to go on searching". `false` is only returned if we
156+
/// definitely cannot find anything bad anywhere.
142157
///
143158
/// The default implementations proceed structurally.
144159
trait Qualif {
@@ -291,9 +306,11 @@ trait Qualif {
291306
}
292307
}
293308

294-
/// Constant containing interior mutability (UnsafeCell).
309+
/// Constant containing interior mutability (`UnsafeCell<T>`).
295310
/// This must be ruled out to make sure that evaluating the constant at compile-time
296-
/// and run-time would produce the same result.
311+
/// and run-time would produce the same result. In particular, promotion of temporaries
312+
/// must not change program behavior; if the promoted could be written to, that would
313+
/// be a problem.
297314
struct HasMutInterior;
298315

299316
impl Qualif for HasMutInterior {
@@ -322,10 +339,10 @@ impl Qualif for HasMutInterior {
322339
_ => return true,
323340
}
324341
} else if let ty::Array(_, len) = ty.sty {
325-
// FIXME(eddyb) the `cx.mode == Mode::Fn` condition
342+
// FIXME(eddyb) the `cx.mode == Mode::NonConstFn` condition
326343
// seems unnecessary, given that this is merely a ZST.
327344
match len.assert_usize(cx.tcx) {
328-
Some(0) if cx.mode == Mode::Fn => {},
345+
Some(0) if cx.mode == Mode::NonConstFn => {},
329346
_ => return true,
330347
}
331348
} else {
@@ -351,9 +368,10 @@ impl Qualif for HasMutInterior {
351368
}
352369
}
353370

354-
/// Constant containing an ADT that implements Drop.
355-
/// This must be ruled out because we cannot run `Drop` during compile-time
356-
/// as that might not be a `const fn`.
371+
/// Constant containing an ADT that implements `Drop`.
372+
/// This must be ruled out (a) because we cannot run `Drop` during compile-time
373+
/// as that might not be a `const fn`, and (b) because implicit promotion would
374+
/// remove side-effects that occur as part of dropping that value.
357375
struct NeedsDrop;
358376

359377
impl Qualif for NeedsDrop {
@@ -376,11 +394,12 @@ impl Qualif for NeedsDrop {
376394
}
377395
}
378396

379-
/// Not promotable at all - non-`const fn` calls, asm!,
397+
/// Not promotable at all - non-`const fn` calls, `asm!`,
380398
/// pointer comparisons, ptr-to-int casts, etc.
381399
/// Inside a const context all constness rules apply, so promotion simply has to follow the regular
382400
/// constant rules (modulo interior mutability or `Drop` rules which are handled `HasMutInterior`
383-
/// and `NeedsDrop` respectively).
401+
/// and `NeedsDrop` respectively). Basically this duplicates the checks that the const-checking
402+
/// visitor enforces by emitting errors when working in const context.
384403
struct IsNotPromotable;
385404

386405
impl Qualif for IsNotPromotable {
@@ -411,9 +430,10 @@ impl Qualif for IsNotPromotable {
411430
ProjectionElem::Index(_) => {}
412431

413432
ProjectionElem::Field(..) => {
414-
if cx.mode == Mode::Fn {
433+
if cx.mode == Mode::NonConstFn {
415434
let base_ty = proj.base.ty(cx.mir, cx.tcx).ty;
416435
if let Some(def) = base_ty.ty_adt_def() {
436+
// No promotion of union field accesses.
417437
if def.is_union() {
418438
return true;
419439
}
@@ -427,7 +447,7 @@ impl Qualif for IsNotPromotable {
427447

428448
fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
429449
match *rvalue {
430-
Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if cx.mode == Mode::Fn => {
450+
Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if cx.mode == Mode::NonConstFn => {
431451
let operand_ty = operand.ty(cx.mir, cx.tcx);
432452
let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
433453
let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
@@ -441,7 +461,7 @@ impl Qualif for IsNotPromotable {
441461
}
442462
}
443463

444-
Rvalue::BinaryOp(op, ref lhs, _) if cx.mode == Mode::Fn => {
464+
Rvalue::BinaryOp(op, ref lhs, _) if cx.mode == Mode::NonConstFn => {
445465
if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(cx.mir, cx.tcx).sty {
446466
assert!(op == BinOp::Eq || op == BinOp::Ne ||
447467
op == BinOp::Le || op == BinOp::Lt ||
@@ -524,9 +544,9 @@ impl Qualif for IsNotPromotable {
524544

525545
/// Refers to temporaries which cannot be promoted *implicitly*.
526546
/// Explicit promotion happens e.g. for constant arguments declared via `rustc_args_required_const`.
527-
/// Implicit promotion has almost the same rules, except that it does not happen if `const fn`
528-
/// calls are involved. The call may be perfectly alright at runtime, but fail at compile time
529-
/// e.g. due to addresses being compared inside the function.
547+
/// Implicit promotion has almost the same rules, except that disallows `const fn` except for
548+
/// those marked `#[rustc_promotable]`. This is to avoid changing a legitimate run-time operation
549+
/// into a failing compile-time operation e.g. due to addresses being compared inside the function.
530550
struct IsNotImplicitlyPromotable;
531551

532552
impl Qualif for IsNotImplicitlyPromotable {
@@ -538,7 +558,7 @@ impl Qualif for IsNotImplicitlyPromotable {
538558
args: &[Operand<'tcx>],
539559
_return_ty: Ty<'tcx>,
540560
) -> bool {
541-
if cx.mode == Mode::Fn {
561+
if cx.mode == Mode::NonConstFn {
542562
if let ty::FnDef(def_id, _) = callee.ty(cx.mir, cx.tcx).sty {
543563
// Never promote runtime `const fn` calls of
544564
// functions without `#[rustc_promotable]`.
@@ -602,8 +622,8 @@ impl ConstCx<'_, 'tcx> {
602622
/// Checks MIR for const-correctness, using `ConstCx`
603623
/// for value qualifications, and accumulates writes of
604624
/// rvalue/call results to locals, in `local_qualif`.
605-
/// For functions (constant or not), it also records
606-
/// candidates for promotion in `promotion_candidates`.
625+
/// It also records candidates for promotion in `promotion_candidates`,
626+
/// both in functions and const/static items.
607627
struct Checker<'a, 'tcx> {
608628
cx: ConstCx<'a, 'tcx>,
609629

@@ -687,7 +707,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
687707
// slightly pointless (even with feature-gating).
688708
fn not_const(&mut self) {
689709
unleash_miri!(self);
690-
if self.mode != Mode::Fn {
710+
if self.mode.requires_const_checking() {
691711
let mut err = struct_span_err!(
692712
self.tcx.sess,
693713
self.span,
@@ -722,7 +742,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
722742
qualifs[HasMutInterior] = false;
723743
qualifs[IsNotPromotable] = true;
724744

725-
if self.mode != Mode::Fn {
745+
if self.mode.requires_const_checking() {
726746
if let BorrowKind::Mut { .. } = kind {
727747
let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
728748
"references in {}s may only refer \
@@ -752,7 +772,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
752772

753773
// We might have a candidate for promotion.
754774
let candidate = Candidate::Ref(location);
755-
// We can only promote interior borrows of promotable temps.
775+
// Start by traversing to the "base", with non-deref projections removed.
756776
let mut place = place;
757777
while let Place::Projection(ref proj) = *place {
758778
if proj.elem == ProjectionElem::Deref {
@@ -761,6 +781,10 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
761781
place = &proj.base;
762782
}
763783
debug!("qualify_consts: promotion candidate: place={:?}", place);
784+
// We can only promote interior borrows of promotable temps (non-temps
785+
// don't get promoted anyway).
786+
// (If we bailed out of the loop due to a `Deref` above, we will definitely
787+
// not enter the conditional here.)
764788
if let Place::Base(PlaceBase::Local(local)) = *place {
765789
if self.mir.local_kind(local) == LocalKind::Temp {
766790
debug!("qualify_consts: promotion candidate: local={:?}", local);
@@ -771,10 +795,11 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
771795
// `HasMutInterior`, from a type that does, e.g.:
772796
// `let _: &'static _ = &(Cell::new(1), 2).1;`
773797
let mut local_qualifs = self.qualifs_in_local(local);
774-
local_qualifs[HasMutInterior] = false;
775-
// Make sure there is no reason to prevent promotion.
798+
// Any qualifications, except HasMutInterior (see above), disqualify
799+
// from promotion.
776800
// This is, in particular, the "implicit promotion" version of
777801
// the check making sure that we don't run drop glue during const-eval.
802+
local_qualifs[HasMutInterior] = false;
778803
if !local_qualifs.0.iter().any(|&qualif| qualif) {
779804
debug!("qualify_consts: promotion candidate: {:?}", candidate);
780805
self.promotion_candidates.push(candidate);
@@ -821,7 +846,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
821846
debug!("store to {:?} {:?}", kind, index);
822847

823848
// Only handle promotable temps in non-const functions.
824-
if self.mode == Mode::Fn {
849+
if self.mode == Mode::NonConstFn {
825850
if kind != LocalKind::Temp ||
826851
!self.temp_promotion_state[index].is_promotable() {
827852
return;
@@ -956,7 +981,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
956981
.get_attrs(*def_id)
957982
.iter()
958983
.any(|attr| attr.check_name(sym::thread_local)) {
959-
if self.mode != Mode::Fn {
984+
if self.mode.requires_const_checking() {
960985
span_err!(self.tcx.sess, self.span, E0625,
961986
"thread-local statics cannot be \
962987
accessed at compile-time");
@@ -980,7 +1005,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
9801005
}
9811006
unleash_miri!(self);
9821007

983-
if self.mode != Mode::Fn {
1008+
if self.mode.requires_const_checking() {
9841009
let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
9851010
"{}s cannot refer to statics, use \
9861011
a constant instead", self.mode);
@@ -1018,7 +1043,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
10181043
}
10191044
let base_ty = proj.base.ty(self.mir, self.tcx).ty;
10201045
match self.mode {
1021-
Mode::Fn => {},
1046+
Mode::NonConstFn => {},
10221047
_ => {
10231048
if let ty::RawPtr(_) = base_ty.sty {
10241049
if !self.tcx.features().const_raw_ptr_deref {
@@ -1054,7 +1079,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
10541079
}
10551080
},
10561081

1057-
| Mode::Fn
1082+
| Mode::NonConstFn
10581083
| Mode::Static
10591084
| Mode::StaticMut
10601085
| Mode::Const
@@ -1144,7 +1169,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
11441169
let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
11451170
match (cast_in, cast_out) {
11461171
(CastTy::Ptr(_), CastTy::Int(_)) |
1147-
(CastTy::FnPtr, CastTy::Int(_)) if self.mode != Mode::Fn => {
1172+
(CastTy::FnPtr, CastTy::Int(_)) if self.mode != Mode::NonConstFn => {
11481173
unleash_miri!(self);
11491174
if !self.tcx.features().const_raw_ptr_to_usize_cast {
11501175
// in const fn and constants require the feature gate
@@ -1171,7 +1196,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
11711196
op == BinOp::Offset);
11721197

11731198
unleash_miri!(self);
1174-
if self.mode != Mode::Fn && !self.tcx.features().const_compare_raw_pointers {
1199+
if self.mode.requires_const_checking() &&
1200+
!self.tcx.features().const_compare_raw_pointers
1201+
{
11751202
// require the feature gate inside constants and const fn
11761203
// FIXME: make it unsafe to use these operations
11771204
emit_feature_err(
@@ -1187,7 +1214,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
11871214

11881215
Rvalue::NullaryOp(NullOp::Box, _) => {
11891216
unleash_miri!(self);
1190-
if self.mode != Mode::Fn {
1217+
if self.mode.requires_const_checking() {
11911218
let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
11921219
"allocations are not allowed in {}s", self.mode);
11931220
err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
@@ -1232,8 +1259,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
12321259
// special intrinsic that can be called diretly without an intrinsic
12331260
// feature gate needs a language feature gate
12341261
"transmute" => {
1235-
// never promote transmute calls
1236-
if self.mode != Mode::Fn {
1262+
if self.mode.requires_const_checking() {
12371263
// const eval transmute calls only with the feature gate
12381264
if !self.tcx.features().const_transmute {
12391265
emit_feature_err(
@@ -1256,7 +1282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
12561282
}
12571283
_ => {
12581284
// In normal functions no calls are feature-gated.
1259-
if self.mode != Mode::Fn {
1285+
if self.mode.requires_const_checking() {
12601286
let unleash_miri = self
12611287
.tcx
12621288
.sess
@@ -1315,7 +1341,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
13151341
}
13161342
}
13171343
ty::FnPtr(_) => {
1318-
if self.mode != Mode::Fn {
1344+
if self.mode.requires_const_checking() {
13191345
let mut err = self.tcx.sess.struct_span_err(
13201346
self.span,
13211347
&format!("function pointers are not allowed in const fn"));
@@ -1374,7 +1400,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
13741400
self.super_terminator_kind(kind, location);
13751401

13761402
// Deny *any* live drops anywhere other than functions.
1377-
if self.mode != Mode::Fn {
1403+
if self.mode.requires_const_checking() {
13781404
unleash_miri!(self);
13791405
// HACK(eddyb): emulate a bit of dataflow analysis,
13801406
// conservatively, that drop elaboration will do.
@@ -1485,12 +1511,12 @@ impl MirPass for QualifyAndPromoteConstants {
14851511
let id = tcx.hir().as_local_hir_id(def_id).unwrap();
14861512
let mut const_promoted_temps = None;
14871513
let mode = match tcx.hir().body_owner_kind_by_hir_id(id) {
1488-
hir::BodyOwnerKind::Closure => Mode::Fn,
1514+
hir::BodyOwnerKind::Closure => Mode::NonConstFn,
14891515
hir::BodyOwnerKind::Fn => {
14901516
if tcx.is_const_fn(def_id) {
14911517
Mode::ConstFn
14921518
} else {
1493-
Mode::Fn
1519+
Mode::NonConstFn
14941520
}
14951521
}
14961522
hir::BodyOwnerKind::Const => {
@@ -1502,7 +1528,7 @@ impl MirPass for QualifyAndPromoteConstants {
15021528
};
15031529

15041530
debug!("run_pass: mode={:?}", mode);
1505-
if mode == Mode::Fn || mode == Mode::ConstFn {
1531+
if mode == Mode::NonConstFn || mode == Mode::ConstFn {
15061532
// This is ugly because Checker holds onto mir,
15071533
// which can't be mutated until its scope ends.
15081534
let (temps, candidates) = {

0 commit comments

Comments
 (0)