}
}
-struct Checker<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
+struct State {
+ local_qualif: IndexVec<Local, Option<Qualif>>,
+
+ qualif: Qualif,
+}
+
+impl State {
+ /// Add the given qualification to self.qualif.
+ fn add(&mut self, qualif: Qualif) {
+ self.qualif = self.qualif | qualif;
+ }
+}
+
+struct Checker<'a, 'gcx, 'tcx> {
+ tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
mode: Mode,
span: Span,
def_id: DefId,
mir: &'a Mir<'tcx>,
rpo: ReversePostorder<'a, 'tcx>,
- tcx: TyCtxt<'a, 'gcx, 'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- local_qualif: IndexVec<Local, Option<Qualif>>,
- qualif: Qualif,
+
+ state: State,
temp_promotion_state: IndexVec<Local, TempState>,
- promotion_candidates: Vec<Candidate>
+ promotion_candidates: Vec<Candidate>,
}
macro_rules! unleash_miri {
rpo,
tcx,
param_env,
- local_qualif,
- qualif: Qualif::empty(),
+ state: State {
+ local_qualif,
+ qualif: Qualif::empty(),
+ },
temp_promotion_state: temps,
promotion_candidates: vec![]
}
// slightly pointless (even with feature-gating).
fn not_const(&mut self) {
unleash_miri!(self);
- self.add(Qualif::NOT_CONST);
+ self.state.add(Qualif::NOT_CONST);
if self.mode != Mode::Fn {
let mut err = struct_span_err!(
self.tcx.sess,
}
}
- /// Adds the given qualification to `self.qualif`.
- fn add(&mut self, qualif: Qualif) {
- self.qualif = self.qualif | qualif;
- }
-
- /// Adds the given type's qualification to `self.qualif`.
+ /// Adds the given type's qualification to self.state.qualif.
fn add_type(&mut self, ty: Ty<'tcx>) {
- self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
- self.qualif.restrict(ty, self.tcx, self.param_env);
+ self.state.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
+ self.state.qualif.restrict(ty, self.tcx, self.param_env);
}
- /// Within the provided closure, `self.qualif` will start
+ /// Within the provided closure, `self.state.qualif` will start
/// out empty, and its value after the closure returns will
/// be combined with the value before the call to nest.
fn nest<F: FnOnce(&mut Self)>(&mut self, f: F) {
- let original = self.qualif;
- self.qualif = Qualif::empty();
+ let original = self.state.qualif;
+ self.state.qualif = Qualif::empty();
f(self);
- self.add(original);
+ self.state.add(original);
}
/// Assign the current qualification to the given destination.
fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
trace!("assign: {:?}", dest);
- let qualif = self.qualif;
+ let qualif = self.state.qualif;
let span = self.span;
let store = |slot: &mut Option<Qualif>| {
if slot.is_some() {
if self.mir.local_kind(index) == LocalKind::Temp
&& self.temp_promotion_state[index].is_promotable() {
debug!("store to promotable temp {:?} ({:?})", index, qualif);
- store(&mut self.local_qualif[index]);
+ store(&mut self.state.local_qualif[index]);
}
}
return;
}
};
debug!("store to var {:?}", index);
- match &mut self.local_qualif[index] {
+ match &mut self.state.local_qualif[index] {
// this is overly restrictive, because even full assignments do not clear the qualif
// While we could special case full assignments, this would be inconsistent with
// aggregates where we overwrite all fields via assignments, which would not get
// that feature.
- Some(ref mut qualif) => *qualif = *qualif | self.qualif,
+ Some(ref mut qualif) => *qualif = *qualif | self.state.qualif,
// insert new qualification
- qualif @ None => *qualif = Some(self.qualif),
+ qualif @ None => *qualif = Some(self.state.qualif),
}
}
}
}
- self.qualif = self.local_qualif[RETURN_PLACE].unwrap_or(Qualif::NOT_CONST);
+ self.state.qualif = self.state.local_qualif[RETURN_PLACE].unwrap_or(Qualif::NOT_CONST);
// Account for errors in consts by using the
// conservative type qualification instead.
- if self.qualif.intersects(Qualif::CONST_ERROR) {
- self.qualif = Qualif::empty();
+ if self.state.qualif.intersects(Qualif::CONST_ERROR) {
+ self.state.qualif = Qualif::empty();
let return_ty = mir.return_ty();
self.add_type(return_ty);
}
}
}
- (self.qualif, Lrc::new(promoted_temps))
+ (self.state.qualif, Lrc::new(promoted_temps))
}
fn is_const_panic_fn(&self, def_id: DefId) -> bool {
}
}
-/// Accumulates an Rvalue or Call's effects in self.qualif.
+/// Accumulates an Rvalue or Call's effects in self.state.qualif.
/// For functions (constant or not), it also records
/// candidates for promotion in promotion_candidates.
impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx, 'tcx> {
self.not_const();
}
LocalKind::Var if self.mode == Mode::Fn => {
- self.add(Qualif::NOT_CONST);
+ self.state.add(Qualif::NOT_CONST);
}
LocalKind::Var |
LocalKind::Arg |
LocalKind::Temp => {
if let LocalKind::Arg = kind {
- self.add(Qualif::FN_ARGUMENT);
+ self.state.add(Qualif::FN_ARGUMENT);
}
if !self.temp_promotion_state[local].is_promotable() {
debug!("visit_local: (not promotable) local={:?}", local);
- self.add(Qualif::NOT_PROMOTABLE);
+ self.state.add(Qualif::NOT_PROMOTABLE);
}
- if let Some(qualif) = self.local_qualif[local] {
- self.add(qualif);
+ if let Some(qualif) = self.state.local_qualif[local] {
+ self.state.add(qualif);
} else {
self.not_const();
}
"thread-local statics cannot be \
accessed at compile-time");
}
- self.add(Qualif::NOT_CONST);
+ self.state.add(Qualif::NOT_CONST);
return;
}
return;
}
unleash_miri!(self);
- self.add(Qualif::NOT_CONST);
+ self.state.add(Qualif::NOT_CONST);
if self.mode != Mode::Fn {
let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
this.not_const()
} else {
// just make sure this doesn't get promoted
- this.add(Qualif::NOT_CONST);
+ this.state.add(Qualif::NOT_CONST);
}
let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
match this.mode {
}
let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx);
- this.qualif.restrict(ty, this.tcx, this.param_env);
+ this.state.qualif.restrict(ty, this.tcx, this.param_env);
}
ProjectionElem::Downcast(..) => {
Operand::Move(_) => {
// Mark the consumed locals to indicate later drops are noops.
if let Operand::Move(Place::Local(local)) = *operand {
- self.local_qualif[local] = self.local_qualif[local].map(|q|
+ self.state.local_qualif[local] = self.state.local_qualif[local].map(|q|
q - Qualif::NEEDS_DROP
);
}
let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(*def_id);
let qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif");
- self.add(qualif);
+ self.state.add(qualif);
// Just in case the type is more specific than
// the definition, e.g., impl associated const
// with type parameters, take it into account.
- self.qualif.restrict(constant.ty, self.tcx, self.param_env);
+ self.state.qualif.restrict(constant.ty, self.tcx, self.param_env);
}
}
}
if forbidden_mut {
unleash_miri!(self);
- self.add(Qualif::NOT_CONST);
+ self.state.add(Qualif::NOT_CONST);
if self.mode != Mode::Fn {
let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
"references in {}s may only refer \
// Constants cannot be borrowed if they contain interior mutability as
// it means that our "silent insertion of statics" could change
// initializer values (very bad).
- if self.qualif.contains(Qualif::MUTABLE_INTERIOR) {
+ if self.state.qualif.contains(Qualif::MUTABLE_INTERIOR) {
// A reference of a MUTABLE_INTERIOR place is instead
// NOT_CONST (see `if forbidden_mut` below), to avoid
// duplicate errors (from reborrowing, for example).
- self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
+ self.state.qualif = self.state.qualif - Qualif::MUTABLE_INTERIOR;
if self.mode != Mode::Fn {
span_err!(self.tcx.sess, self.span, E0492,
"cannot borrow a constant which may contain \
debug!("visit_rvalue: forbidden_mut={:?}", forbidden_mut);
if forbidden_mut {
unleash_miri!(self);
- self.add(Qualif::NOT_CONST);
+ self.state.add(Qualif::NOT_CONST);
} else {
// We might have a candidate for promotion.
let candidate = Candidate::Ref(location);
if let Place::Local(local) = *place {
if self.mir.local_kind(local) == LocalKind::Temp {
debug!("visit_rvalue: local={:?}", local);
- if let Some(qualif) = self.local_qualif[local] {
+ if let Some(qualif) = self.state.local_qualif[local] {
// `forbidden_mut` is false, so we can safely ignore
// `MUTABLE_INTERIOR` from the local's qualifications.
// This allows borrowing fields which don't have
unleash_miri!(self);
if let Mode::Fn = self.mode {
// in normal functions, mark such casts as not promotable
- self.add(Qualif::NOT_CONST);
+ self.state.add(Qualif::NOT_CONST);
} else if !self.tcx.features().const_raw_ptr_to_usize_cast {
// in const fn and constants require the feature gate
// FIXME: make it unsafe inside const fn and constants
unleash_miri!(self);
if let Mode::Fn = self.mode {
// raw pointer operations are not allowed inside promoteds
- self.add(Qualif::NOT_CONST);
+ self.state.add(Qualif::NOT_CONST);
} else if !self.tcx.features().const_compare_raw_pointers {
// require the feature gate inside constants and const fn
// FIXME: make it unsafe to use these operations
Rvalue::NullaryOp(NullOp::Box, _) => {
unleash_miri!(self);
- self.add(Qualif::NOT_CONST);
+ self.state.add(Qualif::NOT_CONST);
if self.mode != Mode::Fn {
let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
"allocations are not allowed in {}s", self.mode);
Rvalue::Aggregate(ref kind, _) => {
if let AggregateKind::Adt(def, ..) = **kind {
if def.has_dtor(self.tcx) {
- self.add(Qualif::NEEDS_DROP);
+ self.state.add(Qualif::NEEDS_DROP);
}
if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() {
let ty = rvalue.ty(self.mir, self.tcx);
self.add_type(ty);
- assert!(self.qualif.contains(Qualif::MUTABLE_INTERIOR));
+ assert!(self.state.qualif.contains(Qualif::MUTABLE_INTERIOR));
}
}
}
}
let candidate = Candidate::Argument { bb, index: i };
if is_shuffle && i == 2 {
- if this.qualif.is_empty() {
+ if this.state.qualif.is_empty() {
debug!("visit_terminator_kind: candidate={:?}", candidate);
this.promotion_candidates.push(candidate);
} else {
// which happens even without the user requesting it.
// We can error out with a hard error if the argument is not
// constant here.
- if (this.qualif - Qualif::NOT_PROMOTABLE).is_empty() {
+ if (this.state.qualif - Qualif::NOT_PROMOTABLE).is_empty() {
debug!("visit_terminator_kind: candidate={:?}", candidate);
this.promotion_candidates.push(candidate);
} else {
// non-const fn calls
if !is_const_fn {
- self.qualif = Qualif::NOT_CONST;
+ self.state.qualif = Qualif::NOT_CONST;
if self.mode != Mode::Fn {
self.tcx.sess.delay_span_bug(
self.span,
if let Some((ref dest, _)) = *destination {
// Avoid propagating irrelevant callee/argument qualifications.
- if self.qualif.intersects(Qualif::CONST_ERROR) {
- self.qualif = Qualif::NOT_CONST;
+ if self.state.qualif.intersects(Qualif::CONST_ERROR) {
+ self.state.qualif = Qualif::NOT_CONST;
} else {
// Be conservative about the returned value of a const fn.
let tcx = self.tcx;
let ty = dest.ty(self.mir, tcx).to_ty(tcx);
if is_const_fn && !is_promotable_const_fn && self.mode == Mode::Fn {
- self.qualif = Qualif::NOT_PROMOTABLE;
+ self.state.qualif = Qualif::NOT_PROMOTABLE;
} else {
- self.qualif = Qualif::empty();
+ self.state.qualif = Qualif::empty();
}
self.add_type(ty);
}
// HACK(eddyb): emulate a bit of dataflow analysis,
// conservatively, that drop elaboration will do.
let needs_drop = if let Place::Local(local) = *place {
- if self.local_qualif[local].map_or(true, |q| q.contains(Qualif::NEEDS_DROP)) {
+ let local_needs_drop = self.state.local_qualif[local]
+ .map_or(true, |q| q.contains(Qualif::NEEDS_DROP));
+ if local_needs_drop {
Some(self.mir.local_decls[local].source_info.span)
} else {
None