From 748bbf259a4835792a37558def86d5ef85a93de3 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Tue, 22 Oct 2019 14:55:04 -0700 Subject: [PATCH] Deduplicate `promote_consts::Validator` and `check_consts::Item` --- src/librustc_mir/transform/promote_consts.rs | 78 ++++++++------------ 1 file changed, 30 insertions(+), 48 deletions(-) diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 4bf1fc60fe7b..a3d535188f40 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -12,7 +12,6 @@ //! 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; @@ -30,7 +29,7 @@ use rustc_target::spec::abi::Abi; use std::{iter, mem, usize}; -use crate::transform::check_consts::{qualifs, Item as ConstCx}; +use crate::transform::check_consts::{qualifs, Item, ConstKind}; /// State of a temporary during collection and promotion. #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -224,18 +223,13 @@ pub fn collect_temps_and_candidates( (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, - // 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` @@ -245,6 +239,14 @@ struct Validator<'a, 'tcx> { explicit: bool, } +impl std::ops::Deref for Validator<'a, 'tcx> { + type Target = Item<'a, 'tcx>; + + fn deref(&self) -> &Self::Target { + &self.item + } +} + struct Unpromotable; impl<'tcx> Validator<'_, 'tcx> { @@ -317,13 +319,14 @@ impl<'tcx> Validator<'_, 'tcx> { if self.qualif_local::(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(_) => {} @@ -333,7 +336,7 @@ impl<'tcx> Validator<'_, 'tcx> { // 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 { @@ -386,7 +389,7 @@ impl<'tcx> Validator<'_, 'tcx> { 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", @@ -398,7 +401,7 @@ impl<'tcx> Validator<'_, 'tcx> { 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); @@ -462,8 +465,8 @@ impl<'tcx> Validator<'_, 'tcx> { } => { // 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); } @@ -490,7 +493,7 @@ impl<'tcx> Validator<'_, 'tcx> { } 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() { @@ -545,7 +548,7 @@ impl<'tcx> Validator<'_, 'tcx> { 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"); @@ -559,7 +562,7 @@ impl<'tcx> Validator<'_, 'tcx> { } } - 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 || @@ -600,17 +603,17 @@ impl<'tcx> Validator<'_, 'tcx> { // 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 { @@ -683,7 +686,7 @@ impl<'tcx> Validator<'_, 'tcx> { ) -> 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]`. @@ -714,6 +717,7 @@ impl<'tcx> Validator<'_, 'tcx> { } } +// FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`. pub fn validate_candidates( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -722,33 +726,11 @@ pub fn validate_candidates( candidates: &[Candidate], ) -> Vec { 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(_) |