//! initialization and can otherwise silence errors, if
//! move analysis runs after promotion on broken MIR.
-use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::mir::*;
use rustc::mir::interpret::ConstValue;
use std::{iter, mem, usize};
-use crate::transform::check_consts::{qualifs, Item as ConstCx};
+use crate::transform::check_consts::{qualifs, Item, ConstKind, is_lang_panic_fn};
/// State of a temporary during collection and promotion.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
Argument { bb: BasicBlock, index: usize },
}
+impl Candidate {
+ /// Returns `true` if we should use the "explicit" rules for promotability for this `Candidate`.
+ fn forces_explicit_promotion(&self) -> bool {
+ match self {
+ Candidate::Ref(_) |
+ Candidate::Repeat(_) => false,
+ Candidate::Argument { .. } => true,
+ }
+ }
+}
+
fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Vec<usize>> {
let attrs = tcx.get_attrs(def_id);
let attr = attrs.iter().find(|a| a.check_name(sym::rustc_args_required_const))?;
(collector.temps, collector.candidates)
}
+/// Checks whether locals that appear in a promotion context (`Candidate`) are actually promotable.
+///
+/// This wraps an `Item`, and has access to all fields of that `Item` via `Deref` coercion.
struct Validator<'a, 'tcx> {
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- body: &'a Body<'tcx>,
- is_static: bool,
- is_static_mut: bool,
- is_non_const_fn: bool,
+ item: Item<'a, 'tcx>,
temps: &'a IndexVec<Local, TempState>,
- // FIXME(eddyb) deduplicate the data in this vs other fields.
- const_cx: ConstCx<'a, 'tcx>,
-
/// Explicit promotion happens e.g. for constant arguments declared via
/// `rustc_args_required_const`.
/// Implicit promotion has almost the same rules, except that disallows `const fn`
explicit: bool,
}
-struct Unpromotable;
+impl std::ops::Deref for Validator<'a, 'tcx> {
+ type Target = Item<'a, 'tcx>;
-impl<'tcx> Validator<'_, 'tcx> {
- fn is_const_panic_fn(&self, def_id: DefId) -> bool {
- Some(def_id) == self.tcx.lang_items().panic_fn() ||
- Some(def_id) == self.tcx.lang_items().begin_panic_fn()
+ fn deref(&self) -> &Self::Target {
+ &self.item
}
+}
+
+struct Unpromotable;
+impl<'tcx> Validator<'_, 'tcx> {
fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> {
match candidate {
Candidate::Ref(loc) => {
if self.qualif_local::<qualifs::NeedsDrop>(base) {
return Err(Unpromotable);
}
+
if let BorrowKind::Mut { .. } = kind {
let ty = place.ty(self.body, self.tcx).ty;
// In theory, any zero-sized value could be borrowed
// mutably without consequences. However, only &mut []
// is allowed right now, and only in functions.
- if self.is_static_mut {
+ if self.const_kind == Some(ConstKind::StaticMut) {
// Inside a `static mut`, &mut [...] is also allowed.
match ty.kind {
ty::Array(..) | ty::Slice(_) => {}
// FIXME(eddyb) the `self.is_non_const_fn` condition
// seems unnecessary, given that this is merely a ZST.
match len.try_eval_usize(self.tcx, self.param_env) {
- Some(0) if self.is_non_const_fn => {},
+ Some(0) if self.const_kind.is_none() => {},
_ => return Err(Unpromotable),
}
} else {
let statement = &self.body[loc.block].statements[loc.statement_index];
match &statement.kind {
StatementKind::Assign(box(_, rhs)) => {
- Q::in_rvalue(&self.const_cx, per_local, rhs)
+ Q::in_rvalue(&self.item, per_local, rhs)
}
_ => {
span_bug!(statement.source_info.span, "{:?} is not an assignment",
match &terminator.kind {
TerminatorKind::Call { func, args, .. } => {
let return_ty = self.body.local_decls[local].ty;
- Q::in_call(&self.const_cx, per_local, func, args, return_ty)
+ Q::in_call(&self.item, per_local, func, args, return_ty)
}
kind => {
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
} => {
// Only allow statics (not consts) to refer to other statics.
// FIXME(eddyb) does this matter at all for promotion?
- let allowed = self.is_static || self.is_static_mut;
- if !allowed {
+ let is_static = self.const_kind.map_or(false, |k| k.is_static());
+ if !is_static {
return Err(Unpromotable);
}
}
ProjectionElem::Field(..) => {
- if self.is_non_const_fn {
+ if self.const_kind.is_none() {
let base_ty =
Place::ty_from(place.base, proj_base, self.body, self.tcx).ty;
if let Some(def) = base_ty.ty_adt_def() {
fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> {
match *rvalue {
- Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if self.is_non_const_fn => {
+ Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if self.const_kind.is_none() => {
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");
}
}
- Rvalue::BinaryOp(op, ref lhs, _) if self.is_non_const_fn => {
+ Rvalue::BinaryOp(op, ref lhs, _) if self.const_kind.is_none() => {
if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind {
assert!(op == BinOp::Eq || op == BinOp::Ne ||
op == BinOp::Le || op == BinOp::Lt ||
// In theory, any zero-sized value could be borrowed
// mutably without consequences. However, only &mut []
// is allowed right now, and only in functions.
- if self.is_static_mut {
+ if self.const_kind == Some(ConstKind::StaticMut) {
// Inside a `static mut`, &mut [...] is also allowed.
match ty.kind {
ty::Array(..) | ty::Slice(_) => {}
_ => return Err(Unpromotable),
}
} else if let ty::Array(_, len) = ty.kind {
- // FIXME(eddyb) the `self.is_non_const_fn` condition
- // seems unnecessary, given that this is merely a ZST.
+ // FIXME(eddyb): We only return `Unpromotable` for `&mut []` inside a
+ // const context which seems unnecessary given that this is merely a ZST.
match len.try_eval_usize(self.tcx, self.param_env) {
- Some(0) if self.is_non_const_fn => {},
+ Some(0) if self.const_kind.is_none() => {},
_ => return Err(Unpromotable),
}
} else {
) -> Result<(), Unpromotable> {
let fn_ty = callee.ty(self.body, self.tcx);
- if !self.explicit && self.is_non_const_fn {
+ if !self.explicit && self.const_kind.is_none() {
if let ty::FnDef(def_id, _) = fn_ty.kind {
// Never promote runtime `const fn` calls of
// functions without `#[rustc_promotable]`.
ty::FnDef(def_id, _) => {
self.tcx.is_const_fn(def_id) ||
self.tcx.is_unstable_const_fn(def_id).is_some() ||
- self.is_const_panic_fn(def_id)
+ is_lang_panic_fn(self.tcx, self.def_id)
}
_ => false,
};
}
}
+// FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`.
pub fn validate_candidates(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
candidates: &[Candidate],
) -> Vec<Candidate> {
let mut validator = Validator {
- tcx,
- param_env: tcx.param_env(def_id),
- body,
- is_static: false,
- is_static_mut: false,
- is_non_const_fn: false,
+ item: Item::new(tcx, def_id, body),
temps,
-
- const_cx: ConstCx::new(tcx, def_id, body),
-
explicit: false,
};
- // FIXME(eddyb) remove the distinctions that make this necessary.
- let id = tcx.hir().as_local_hir_id(def_id).unwrap();
- match tcx.hir().body_owner_kind(id) {
- hir::BodyOwnerKind::Closure => validator.is_non_const_fn = true,
- hir::BodyOwnerKind::Fn => {
- if !tcx.is_const_fn(def_id) {
- validator.is_non_const_fn = true;
- }
- },
- hir::BodyOwnerKind::Static(hir::MutImmutable) => validator.is_static = true,
- hir::BodyOwnerKind::Static(hir::MutMutable) => validator.is_static_mut = true,
- _ => {}
- }
-
candidates.iter().copied().filter(|&candidate| {
- validator.explicit = match candidate {
- Candidate::Ref(_) |
- Candidate::Repeat(_) => false,
- Candidate::Argument { .. } => true,
- };
+ validator.explicit = candidate.forces_explicit_promotion();
// FIXME(eddyb) also emit the errors for shuffle indices
// and `#[rustc_args_required_const]` arguments here.
- validator.validate_candidate(candidate).is_ok()
+ let is_promotable = validator.validate_candidate(candidate).is_ok();
+ match candidate {
+ Candidate::Argument { bb, index } if !is_promotable => {
+ let span = body[bb].terminator().source_info.span;
+ let msg = format!("argument {} is required to be a constant", index + 1);
+ tcx.sess.span_err(span, &msg);
+ }
+ _ => ()
+ }
+
+ is_promotable
}).collect()
}