/// What kind of item we are in.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum Mode {
- Const,
+ /// A `static` item.
Static,
+ /// A `static mut` item.
StaticMut,
+ /// A `const fn` item.
ConstFn,
- Fn
+ /// A `const` item or an anonymous constant (e.g. in array lengths).
+ Const,
+ /// Other type of `fn`.
+ NonConstFn,
+}
+
+impl Mode {
+ /// Determine whether we have to do full const-checking because syntactically, we
+ /// are required to be "const".
+ #[inline]
+ fn requires_const_checking(self) -> bool {
+ self != Mode::NonConstFn
+ }
}
impl fmt::Display for Mode {
Mode::Const => write!(f, "constant"),
Mode::Static | Mode::StaticMut => write!(f, "static"),
Mode::ConstFn => write!(f, "constant function"),
- Mode::Fn => write!(f, "function")
+ Mode::NonConstFn => write!(f, "function")
}
}
}
tcx: TyCtxt<'a, 'tcx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
mode: Mode,
- mir: &'a Body<'tcx>,
+ body: &'a Body<'tcx>,
per_local: PerQualif<BitSet<Local>>,
}
},
}
+/// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
+/// code for promotion or prevent it from evaluating at compile time. So `return true` means
+/// "I found something bad, no reason to go on searching". `false` is only returned if we
+/// definitely cannot find anything bad anywhere.
+///
+/// The default implementations proceed structurally.
trait Qualif {
const IDX: usize;
let base_qualif = Self::in_place(cx, &proj.base);
let qualif = base_qualif && Self::mask_for_ty(
cx,
- proj.base.ty(cx.mir, cx.tcx)
+ proj.base.ty(cx.body, cx.tcx)
.projection_ty(cx.tcx, &proj.elem)
.ty,
);
// Special-case reborrows to be more like a copy of the reference.
if let Place::Projection(ref proj) = *place {
if let ProjectionElem::Deref = proj.elem {
- let base_ty = proj.base.ty(cx.mir, cx.tcx).ty;
+ let base_ty = proj.base.ty(cx.body, cx.tcx).ty;
if let ty::Ref(..) = base_ty.sty {
return Self::in_place(cx, &proj.base);
}
}
}
-// Constant containing interior mutability (UnsafeCell).
+/// Constant containing interior mutability (`UnsafeCell<T>`).
+/// This must be ruled out to make sure that evaluating the constant at compile-time
+/// and at *any point* during the run-time would produce the same result. In particular,
+/// promotion of temporaries must not change program behavior; if the promoted could be
+/// written to, that would be a problem.
struct HasMutInterior;
impl Qualif for HasMutInterior {
// allowed in constants (and the `Checker` will error), and/or it
// won't be promoted, due to `&mut ...` or interior mutability.
Rvalue::Ref(_, kind, ref place) => {
- let ty = place.ty(cx.mir, cx.tcx).ty;
+ let ty = place.ty(cx.body, cx.tcx).ty;
if let BorrowKind::Mut { .. } = kind {
// In theory, any zero-sized value could be borrowed
_ => return true,
}
} else if let ty::Array(_, len) = ty.sty {
- // FIXME(eddyb) the `cx.mode == Mode::Fn` condition
+ // FIXME(eddyb) the `cx.mode == Mode::NonConstFn` condition
// seems unnecessary, given that this is merely a ZST.
match len.assert_usize(cx.tcx) {
- Some(0) if cx.mode == Mode::Fn => {},
+ Some(0) if cx.mode == Mode::NonConstFn => {},
_ => return true,
}
} else {
Rvalue::Aggregate(ref kind, _) => {
if let AggregateKind::Adt(def, ..) = **kind {
if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
- let ty = rvalue.ty(cx.mir, cx.tcx);
+ let ty = rvalue.ty(cx.body, cx.tcx);
assert_eq!(Self::in_any_value_of_ty(cx, ty), Some(true));
return true;
}
}
}
-// Constant containing an ADT that implements Drop.
+/// Constant containing an ADT that implements `Drop`.
+/// This must be ruled out (a) because we cannot run `Drop` during compile-time
+/// as that might not be a `const fn`, and (b) because implicit promotion would
+/// remove side-effects that occur as part of dropping that value.
struct NeedsDrop;
impl Qualif for NeedsDrop {
}
}
-// Not promotable at all - non-`const fn` calls, asm!,
-// pointer comparisons, ptr-to-int casts, etc.
+/// Not promotable at all - non-`const fn` calls, `asm!`,
+/// pointer comparisons, ptr-to-int casts, etc.
+/// Inside a const context all constness rules apply, so promotion simply has to follow the regular
+/// constant rules (modulo interior mutability or `Drop` rules which are handled `HasMutInterior`
+/// and `NeedsDrop` respectively). Basically this duplicates the checks that the const-checking
+/// visitor enforces by emitting errors when working in const context.
struct IsNotPromotable;
impl Qualif for IsNotPromotable {
ProjectionElem::Index(_) => {}
ProjectionElem::Field(..) => {
- if cx.mode == Mode::Fn {
- let base_ty = proj.base.ty(cx.mir, cx.tcx).ty;
+ if cx.mode == Mode::NonConstFn {
+ let base_ty = proj.base.ty(cx.body, cx.tcx).ty;
if let Some(def) = base_ty.ty_adt_def() {
+ // No promotion of union field accesses.
if def.is_union() {
return true;
}
fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
match *rvalue {
- Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if cx.mode == Mode::Fn => {
- let operand_ty = operand.ty(cx.mir, cx.tcx);
+ Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if cx.mode == Mode::NonConstFn => {
+ let operand_ty = operand.ty(cx.body, cx.tcx);
let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
match (cast_in, cast_out) {
}
}
- Rvalue::BinaryOp(op, ref lhs, _) if cx.mode == Mode::Fn => {
- if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(cx.mir, cx.tcx).sty {
+ Rvalue::BinaryOp(op, ref lhs, _) if cx.mode == Mode::NonConstFn => {
+ if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(cx.body, cx.tcx).sty {
assert!(op == BinOp::Eq || op == BinOp::Ne ||
op == BinOp::Le || op == BinOp::Lt ||
op == BinOp::Ge || op == BinOp::Gt ||
args: &[Operand<'tcx>],
_return_ty: Ty<'tcx>,
) -> bool {
- let fn_ty = callee.ty(cx.mir, cx.tcx);
+ let fn_ty = callee.ty(cx.body, cx.tcx);
match fn_ty.sty {
ty::FnDef(def_id, _) => {
match cx.tcx.fn_sig(def_id).abi() {
/// Refers to temporaries which cannot be promoted *implicitly*.
/// Explicit promotion happens e.g. for constant arguments declared via `rustc_args_required_const`.
-/// Inside a const context all constness rules
-/// apply, so implicit promotion simply has to follow the regular constant rules (modulo interior
-/// mutability or `Drop` rules which are handled `HasMutInterior` and `NeedsDrop` respectively).
-/// Implicit promotion inside regular functions does not happen if `const fn` calls are involved,
-/// as the call may be perfectly alright at runtime, but fail at compile time e.g. due to addresses
-/// being compared inside the function.
+/// Implicit promotion has almost the same rules, except that disallows `const fn` except for
+/// those marked `#[rustc_promotable]`. This is to avoid changing a legitimate run-time operation
+/// into a failing compile-time operation e.g. due to addresses being compared inside the function.
struct IsNotImplicitlyPromotable;
impl Qualif for IsNotImplicitlyPromotable {
args: &[Operand<'tcx>],
_return_ty: Ty<'tcx>,
) -> bool {
- if cx.mode == Mode::Fn {
- if let ty::FnDef(def_id, _) = callee.ty(cx.mir, cx.tcx).sty {
+ if cx.mode == Mode::NonConstFn {
+ if let ty::FnDef(def_id, _) = callee.ty(cx.body, cx.tcx).sty {
// Never promote runtime `const fn` calls of
// functions without `#[rustc_promotable]`.
if !cx.tcx.is_promotable_const_fn(def_id) {
}
}
+/// Checks MIR for being admissible as a compile-time constant, using `ConstCx`
+/// for value qualifications, and accumulates writes of
+/// rvalue/call results to locals, in `local_qualif`.
+/// It also records candidates for promotion in `promotion_candidates`,
+/// both in functions and const/static items.
struct Checker<'a, 'tcx> {
cx: ConstCx<'a, 'tcx>,
impl<'a, 'tcx> Checker<'a, 'tcx> {
fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId,
- mir: &'a Body<'tcx>,
+ body: &'a Body<'tcx>,
mode: Mode)
-> Self {
assert!(def_id.is_local());
- let mut rpo = traversal::reverse_postorder(mir);
- let temps = promote_consts::collect_temps(mir, &mut rpo);
+ let mut rpo = traversal::reverse_postorder(body);
+ let temps = promote_consts::collect_temps(body, &mut rpo);
rpo.reset();
let param_env = tcx.param_env(def_id);
tcx,
param_env,
mode,
- mir,
- per_local: PerQualif::new(BitSet::new_empty(mir.local_decls.len())),
+ body,
+ per_local: PerQualif::new(BitSet::new_empty(body.local_decls.len())),
};
- for (local, decl) in mir.local_decls.iter_enumerated() {
- if let LocalKind::Arg = mir.local_kind(local) {
+ for (local, decl) in body.local_decls.iter_enumerated() {
+ if let LocalKind::Arg = body.local_kind(local) {
let qualifs = cx.qualifs_in_any_value_of_ty(decl.ty);
for (per_local, qualif) in &mut cx.per_local.as_mut().zip(qualifs).0 {
if *qualif {
if !temps[local].is_promotable() {
cx.per_local[IsNotPromotable].insert(local);
}
- if let LocalKind::Var = mir.local_kind(local) {
+ if let LocalKind::Var = body.local_kind(local) {
// Sanity check to prevent implicit and explicit promotion of
// named locals
assert!(cx.per_local[IsNotPromotable].contains(local));
Checker {
cx,
- span: mir.span,
+ span: body.span,
def_id,
rpo,
temp_promotion_state: temps,
// slightly pointless (even with feature-gating).
fn not_const(&mut self) {
unleash_miri!(self);
- if self.mode != Mode::Fn {
+ if self.mode.requires_const_checking() {
let mut err = struct_span_err!(
self.tcx.sess,
self.span,
qualifs[HasMutInterior] = false;
qualifs[IsNotPromotable] = true;
- if self.mode != Mode::Fn {
+ if self.mode.requires_const_checking() {
if let BorrowKind::Mut { .. } = kind {
let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
"references in {}s may only refer \
// We might have a candidate for promotion.
let candidate = Candidate::Ref(location);
- // We can only promote interior borrows of promotable temps.
+ // Start by traversing to the "base", with non-deref projections removed.
let mut place = place;
while let Place::Projection(ref proj) = *place {
if proj.elem == ProjectionElem::Deref {
place = &proj.base;
}
debug!("qualify_consts: promotion candidate: place={:?}", place);
+ // We can only promote interior borrows of promotable temps (non-temps
+ // don't get promoted anyway).
+ // (If we bailed out of the loop due to a `Deref` above, we will definitely
+ // not enter the conditional here.)
if let Place::Base(PlaceBase::Local(local)) = *place {
- if self.mir.local_kind(local) == LocalKind::Temp {
+ if self.body.local_kind(local) == LocalKind::Temp {
debug!("qualify_consts: promotion candidate: local={:?}", local);
// The borrowed place doesn't have `HasMutInterior`
// (from `in_rvalue`), so we can safely ignore
// `HasMutInterior`, from a type that does, e.g.:
// `let _: &'static _ = &(Cell::new(1), 2).1;`
let mut local_qualifs = self.qualifs_in_local(local);
+ // Any qualifications, except HasMutInterior (see above), disqualify
+ // from promotion.
+ // This is, in particular, the "implicit promotion" version of
+ // the check making sure that we don't run drop glue during const-eval.
local_qualifs[HasMutInterior] = false;
if !local_qualifs.0.iter().any(|&qualif| qualif) {
debug!("qualify_consts: promotion candidate: {:?}", candidate);
}
};
- let kind = self.mir.local_kind(index);
+ let kind = self.body.local_kind(index);
debug!("store to {:?} {:?}", kind, index);
// Only handle promotable temps in non-const functions.
- if self.mode == Mode::Fn {
+ if self.mode == Mode::NonConstFn {
if kind != LocalKind::Temp ||
!self.temp_promotion_state[index].is_promotable() {
return;
fn check_const(&mut self) -> (u8, &'tcx BitSet<Local>) {
debug!("const-checking {} {:?}", self.mode, self.def_id);
- let mir = self.mir;
+ let body = self.body;
- let mut seen_blocks = BitSet::new_empty(mir.basic_blocks().len());
+ let mut seen_blocks = BitSet::new_empty(body.basic_blocks().len());
let mut bb = START_BLOCK;
loop {
seen_blocks.insert(bb.index());
- self.visit_basic_block_data(bb, &mir[bb]);
+ self.visit_basic_block_data(bb, &body[bb]);
- let target = match mir[bb].terminator().kind {
+ let target = match body[bb].terminator().kind {
TerminatorKind::Goto { target } |
TerminatorKind::Drop { target, .. } |
TerminatorKind::Assert { target, .. } |
for candidate in &self.promotion_candidates {
match *candidate {
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
- match self.mir[bb].statements[stmt_idx].kind {
+ match self.body[bb].statements[stmt_idx].kind {
StatementKind::Assign(
_,
box Rvalue::Ref(_, _, Place::Base(PlaceBase::Local(index)))
// Account for errors in consts by using the
// conservative type qualification instead.
if qualifs[IsNotPromotable] {
- qualifs = self.qualifs_in_any_value_of_ty(mir.return_ty());
+ qualifs = self.qualifs_in_any_value_of_ty(body.return_ty());
}
(qualifs.encode_to_bits(), self.tcx.arena.alloc(promoted_temps))
}
}
-/// Checks MIR for const-correctness, using `ConstCx`
-/// for value qualifications, and accumulates writes of
-/// rvalue/call results to locals, in `local_qualif`.
-/// For functions (constant or not), it also records
-/// candidates for promotion in `promotion_candidates`.
impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
- fn visit_place(&mut self,
- place: &Place<'tcx>,
- context: PlaceContext,
- location: Location) {
- debug!("visit_place: place={:?} context={:?} location={:?}", place, context, location);
- place.iterate(|place_base, place_projections| {
- match place_base {
- PlaceBase::Local(_) => {}
- PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. }) => {
- unreachable!()
- }
- PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. }) => {
- if self.tcx
- .get_attrs(*def_id)
- .iter()
- .any(|attr| attr.check_name(sym::thread_local)) {
- if self.mode != Mode::Fn {
- span_err!(self.tcx.sess, self.span, E0625,
- "thread-local statics cannot be \
- accessed at compile-time");
- }
- return;
+ fn visit_place_base(
+ &mut self,
+ place_base: &PlaceBase<'tcx>,
+ context: PlaceContext,
+ location: Location,
+ ) {
+ self.super_place_base(place_base, context, location);
+ match place_base {
+ PlaceBase::Local(_) => {}
+ PlaceBase::Static(box Static{ kind: StaticKind::Promoted(_), .. }) => {
+ unreachable!()
+ }
+ PlaceBase::Static(box Static{ kind: StaticKind::Static(def_id), .. }) => {
+ if self.tcx
+ .get_attrs(*def_id)
+ .iter()
+ .any(|attr| attr.check_name(sym::thread_local)) {
+ if self.mode.requires_const_checking() {
+ span_err!(self.tcx.sess, self.span, E0625,
+ "thread-local statics cannot be \
+ accessed at compile-time");
}
+ return;
+ }
- // Only allow statics (not consts) to refer to other statics.
- if self.mode == Mode::Static || self.mode == Mode::StaticMut {
- if self.mode == Mode::Static && context.is_mutating_use() {
- // this is not strictly necessary as miri will also bail out
- // For interior mutability we can't really catch this statically as that
- // goes through raw pointers and intermediate temporaries, so miri has
- // to catch this anyway
- self.tcx.sess.span_err(
- self.span,
- "cannot mutate statics in the initializer of another static",
- );
- }
- return;
+ // Only allow statics (not consts) to refer to other statics.
+ if self.mode == Mode::Static || self.mode == Mode::StaticMut {
+ if self.mode == Mode::Static && context.is_mutating_use() {
+ // this is not strictly necessary as miri will also bail out
+ // For interior mutability we can't really catch this statically as that
+ // goes through raw pointers and intermediate temporaries, so miri has
+ // to catch this anyway
+ self.tcx.sess.span_err(
+ self.span,
+ "cannot mutate statics in the initializer of another static",
+ );
}
- unleash_miri!(self);
+ return;
+ }
+ unleash_miri!(self);
- if self.mode != Mode::Fn {
- let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
- "{}s cannot refer to statics, use \
- a constant instead", self.mode);
- if self.tcx.sess.teach(&err.get_code().unwrap()) {
- err.note(
- "Static and const variables can refer to other const variables. \
- But a const variable cannot refer to a static variable."
- );
- err.help(
- "To fix this, the value can be extracted as a const and then used."
- );
- }
- err.emit()
+ if self.mode.requires_const_checking() {
+ let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
+ "{}s cannot refer to statics, use \
+ a constant instead", self.mode);
+ if self.tcx.sess.teach(&err.get_code().unwrap()) {
+ err.note(
+ "Static and const variables can refer to other const variables. \
+ But a const variable cannot refer to a static variable."
+ );
+ err.help(
+ "To fix this, the value can be extracted as a const and then used."
+ );
}
+ err.emit()
}
}
+ }
+ }
- for proj in place_projections {
- match proj.elem {
- ProjectionElem::Deref => {
- if context.is_mutating_use() {
- // `not_const` errors out in const contexts
- self.not_const()
- }
- let base_ty = proj.base.ty(self.mir, self.tcx).ty;
- match self.mode {
- Mode::Fn => {},
- _ => {
- if let ty::RawPtr(_) = base_ty.sty {
- if !self.tcx.features().const_raw_ptr_deref {
- emit_feature_err(
- &self.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
- self.span, GateIssue::Language,
- &format!(
- "dereferencing raw pointers in {}s is unstable",
- self.mode,
- ),
- );
- }
- }
+ fn visit_projection(
+ &mut self,
+ proj: &Projection<'tcx>,
+ context: PlaceContext,
+ location: Location,
+ ) {
+ debug!(
+ "visit_place_projection: proj={:?} context={:?} location={:?}",
+ proj, context, location,
+ );
+ self.super_projection(proj, context, location);
+ match proj.elem {
+ ProjectionElem::Deref => {
+ if context.is_mutating_use() {
+ // `not_const` errors out in const contexts
+ self.not_const()
+ }
+ let base_ty = proj.base.ty(self.body, self.tcx).ty;
+ match self.mode {
+ Mode::NonConstFn => {},
+ _ => {
+ if let ty::RawPtr(_) = base_ty.sty {
+ if !self.tcx.features().const_raw_ptr_deref {
+ emit_feature_err(
+ &self.tcx.sess.parse_sess, sym::const_raw_ptr_deref,
+ self.span, GateIssue::Language,
+ &format!(
+ "dereferencing raw pointers in {}s is unstable",
+ self.mode,
+ ),
+ );
}
}
}
+ }
+ }
- ProjectionElem::ConstantIndex {..} |
- ProjectionElem::Subslice {..} |
- ProjectionElem::Field(..) |
- ProjectionElem::Index(_) => {
- let base_ty = proj.base.ty(self.mir, self.tcx).ty;
- if let Some(def) = base_ty.ty_adt_def() {
- if def.is_union() {
- match self.mode {
- Mode::ConstFn => {
- if !self.tcx.features().const_fn_union {
- emit_feature_err(
- &self.tcx.sess.parse_sess, sym::const_fn_union,
- self.span, GateIssue::Language,
- "unions in const fn are unstable",
- );
- }
- },
-
- | Mode::Fn
- | Mode::Static
- | Mode::StaticMut
- | Mode::Const
- => {},
+ ProjectionElem::ConstantIndex {..} |
+ ProjectionElem::Subslice {..} |
+ ProjectionElem::Field(..) |
+ ProjectionElem::Index(_) => {
+ let base_ty = proj.base.ty(self.body, self.tcx).ty;
+ if let Some(def) = base_ty.ty_adt_def() {
+ if def.is_union() {
+ match self.mode {
+ Mode::ConstFn => {
+ if !self.tcx.features().const_fn_union {
+ emit_feature_err(
+ &self.tcx.sess.parse_sess, sym::const_fn_union,
+ self.span, GateIssue::Language,
+ "unions in const fn are unstable",
+ );
}
- }
- }
- }
+ },
- ProjectionElem::Downcast(..) => {
- self.not_const()
+ | Mode::NonConstFn
+ | Mode::Static
+ | Mode::StaticMut
+ | Mode::Const
+ => {},
+ }
}
}
}
- });
+
+ ProjectionElem::Downcast(..) => {
+ self.not_const()
+ }
+ }
}
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
// Check nested operands and places.
if let Rvalue::Ref(_, kind, ref place) = *rvalue {
// Special-case reborrows.
- let mut is_reborrow = false;
+ let mut reborrow_place = None;
if let Place::Projection(ref proj) = *place {
if let ProjectionElem::Deref = proj.elem {
- let base_ty = proj.base.ty(self.mir, self.tcx).ty;
+ let base_ty = proj.base.ty(self.body, self.tcx).ty;
if let ty::Ref(..) = base_ty.sty {
- is_reborrow = true;
+ reborrow_place = Some(&proj.base);
}
}
}
- if is_reborrow {
+ if let Some(place) = reborrow_place {
let ctx = match kind {
BorrowKind::Shared => PlaceContext::NonMutatingUse(
NonMutatingUseContext::SharedBorrow,
MutatingUseContext::Borrow,
),
};
- self.super_place(place, ctx, location);
+ self.visit_place(place, ctx, location);
} else {
self.super_rvalue(rvalue, location);
}
Rvalue::Aggregate(..) => {}
Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
- let operand_ty = operand.ty(self.mir, self.tcx);
+ let operand_ty = operand.ty(self.body, self.tcx);
let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
match (cast_in, cast_out) {
(CastTy::Ptr(_), CastTy::Int(_)) |
- (CastTy::FnPtr, CastTy::Int(_)) if self.mode != Mode::Fn => {
+ (CastTy::FnPtr, CastTy::Int(_)) if self.mode != Mode::NonConstFn => {
unleash_miri!(self);
if !self.tcx.features().const_raw_ptr_to_usize_cast {
// in const fn and constants require the feature gate
}
Rvalue::BinaryOp(op, ref lhs, _) => {
- if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.mir, self.tcx).sty {
+ if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).sty {
assert!(op == BinOp::Eq || op == BinOp::Ne ||
op == BinOp::Le || op == BinOp::Lt ||
op == BinOp::Ge || op == BinOp::Gt ||
op == BinOp::Offset);
unleash_miri!(self);
- if self.mode != Mode::Fn && !self.tcx.features().const_compare_raw_pointers {
+ if self.mode.requires_const_checking() &&
+ !self.tcx.features().const_compare_raw_pointers
+ {
// require the feature gate inside constants and const fn
// FIXME: make it unsafe to use these operations
emit_feature_err(
Rvalue::NullaryOp(NullOp::Box, _) => {
unleash_miri!(self);
- if self.mode != Mode::Fn {
+ if self.mode.requires_const_checking() {
let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
"allocations are not allowed in {}s", self.mode);
err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
self.assign(dest, ValueSource::Call {
callee: func,
args,
- return_ty: dest.ty(self.mir, self.tcx).ty,
+ return_ty: dest.ty(self.body, self.tcx).ty,
}, location);
}
- let fn_ty = func.ty(self.mir, self.tcx);
+ let fn_ty = func.ty(self.body, self.tcx);
let mut callee_def_id = None;
let mut is_shuffle = false;
match fn_ty.sty {
// special intrinsic that can be called diretly without an intrinsic
// feature gate needs a language feature gate
"transmute" => {
- // never promote transmute calls
- if self.mode != Mode::Fn {
+ if self.mode.requires_const_checking() {
// const eval transmute calls only with the feature gate
if !self.tcx.features().const_transmute {
emit_feature_err(
}
_ => {
// In normal functions no calls are feature-gated.
- if self.mode != Mode::Fn {
+ if self.mode.requires_const_checking() {
let unleash_miri = self
.tcx
.sess
}
}
ty::FnPtr(_) => {
- if self.mode != Mode::Fn {
+ if self.mode.requires_const_checking() {
let mut err = self.tcx.sess.struct_span_err(
self.span,
&format!("function pointers are not allowed in const fn"));
}
}
- if self.mode == Mode::Fn {
+ // No need to do anything in constants and statics, as everything is "constant" anyway
+ // so promotion would be useless.
+ if self.mode != Mode::Static && self.mode != Mode::Const {
let constant_args = callee_def_id.and_then(|id| {
args_required_const(self.tcx, id)
}).unwrap_or_default();
self.super_terminator_kind(kind, location);
// Deny *any* live drops anywhere other than functions.
- if self.mode != Mode::Fn {
+ if self.mode.requires_const_checking() {
unleash_miri!(self);
// HACK(eddyb): emulate a bit of dataflow analysis,
// conservatively, that drop elaboration will do.
let needs_drop = if let Place::Base(PlaceBase::Local(local)) = *place {
if NeedsDrop::in_local(self, local) {
- Some(self.mir.local_decls[local].source_info.span)
+ Some(self.body.local_decls[local].source_info.span)
} else {
None
}
if let Some(span) = needs_drop {
// Double-check the type being dropped, to minimize false positives.
- let ty = place.ty(self.mir, self.tcx).ty;
+ let ty = place.ty(self.body, self.tcx).ty;
if ty.needs_drop(self.tcx, self.param_env) {
struct_span_err!(self.tcx.sess, span, E0493,
"destructors cannot be evaluated at compile-time")
// cannot yet be stolen), because `mir_validated()`, which steals
// from `mir_const(), forces this query to execute before
// performing the steal.
- let mir = &tcx.mir_const(def_id).borrow();
+ let body = &tcx.mir_const(def_id).borrow();
- if mir.return_ty().references_error() {
- tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: MIR had errors");
+ if body.return_ty().references_error() {
+ tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
return (1 << IsNotPromotable::IDX, tcx.arena.alloc(BitSet::new_empty(0)));
}
- Checker::new(tcx, def_id, mir, Mode::Const).check_const()
+ Checker::new(tcx, def_id, body, Mode::Const).check_const()
}
pub struct QualifyAndPromoteConstants;
fn run_pass<'a, 'tcx>(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
src: MirSource<'tcx>,
- mir: &mut Body<'tcx>) {
+ body: &mut Body<'tcx>) {
// There's not really any point in promoting errorful MIR.
- if mir.return_ty().references_error() {
- tcx.sess.delay_span_bug(mir.span, "QualifyAndPromoteConstants: MIR had errors");
+ if body.return_ty().references_error() {
+ tcx.sess.delay_span_bug(body.span, "QualifyAndPromoteConstants: MIR had errors");
return;
}
let id = tcx.hir().as_local_hir_id(def_id).unwrap();
let mut const_promoted_temps = None;
let mode = match tcx.hir().body_owner_kind_by_hir_id(id) {
- hir::BodyOwnerKind::Closure => Mode::Fn,
+ hir::BodyOwnerKind::Closure => Mode::NonConstFn,
hir::BodyOwnerKind::Fn => {
if tcx.is_const_fn(def_id) {
Mode::ConstFn
} else {
- Mode::Fn
+ Mode::NonConstFn
}
}
hir::BodyOwnerKind::Const => {
};
debug!("run_pass: mode={:?}", mode);
- if mode == Mode::Fn || mode == Mode::ConstFn {
+ if mode == Mode::NonConstFn || mode == Mode::ConstFn {
// This is ugly because Checker holds onto mir,
// which can't be mutated until its scope ends.
let (temps, candidates) = {
- let mut checker = Checker::new(tcx, def_id, mir, mode);
+ let mut checker = Checker::new(tcx, def_id, body, mode);
if mode == Mode::ConstFn {
if tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
checker.check_const();
} else if tcx.is_min_const_fn(def_id) {
// enforce `min_const_fn` for stable const fns
use super::qualify_min_const_fn::is_min_const_fn;
- if let Err((span, err)) = is_min_const_fn(tcx, def_id, mir) {
+ if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
let mut diag = struct_span_err!(
tcx.sess,
span,
};
// Do the actual promotion, now that we know what's viable.
- promote_consts::promote_candidates(mir, tcx, temps, candidates);
+ promote_consts::promote_candidates(body, tcx, temps, candidates);
} else {
- if !mir.control_flow_destroyed.is_empty() {
- let mut locals = mir.vars_iter();
+ if !body.control_flow_destroyed.is_empty() {
+ let mut locals = body.vars_iter();
if let Some(local) = locals.next() {
- let span = mir.local_decls[local].source_info.span;
+ let span = body.local_decls[local].source_info.span;
let mut error = tcx.sess.struct_span_err(
span,
&format!(
mode,
),
);
- for (span, kind) in mir.control_flow_destroyed.iter() {
+ for (span, kind) in body.control_flow_destroyed.iter() {
error.span_note(
*span,
&format!("use of {} here does not actually short circuit due to \
);
}
for local in locals {
- let span = mir.local_decls[local].source_info.span;
+ let span = body.local_decls[local].source_info.span;
error.span_note(
span,
"more locals defined here",
// Already computed by `mir_const_qualif`.
const_promoted_temps.unwrap()
} else {
- Checker::new(tcx, def_id, mir, mode).check_const().1
+ Checker::new(tcx, def_id, body, mode).check_const().1
};
// In `const` and `static` everything without `StorageDead`
// is `'static`, we don't have to create promoted MIR fragments,
// just remove `Drop` and `StorageDead` on "promoted" locals.
debug!("run_pass: promoted_temps={:?}", promoted_temps);
- for block in mir.basic_blocks_mut() {
+ for block in body.basic_blocks_mut() {
block.statements.retain(|statement| {
match statement.kind {
StatementKind::StorageDead(index) => {
return;
}
}
- let ty = mir.return_ty();
+ let ty = body.return_ty();
tcx.infer_ctxt().enter(|infcx| {
let param_env = ty::ParamEnv::empty();
- let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
+ let cause = traits::ObligationCause::new(body.span, id, traits::SharedStatic);
let mut fulfillment_cx = traits::FulfillmentContext::new();
fulfillment_cx.register_bound(&infcx,
param_env,