X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=compiler%2Frustc_mir%2Fsrc%2Ftransform%2Fpromote_consts.rs;h=78e84419c62cde8cf85337cb23d6b42725c1d7ba;hb=cc77ba46fcb2d288aa01554b48cd586c5827c3dd;hp=1bbaf833c4fd91710bb8f5c94b1960f31dd7d2f0;hpb=237eab11560e8ea9008921a46ae60518011fe00f;p=rust.git diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index 1bbaf833c4f..78e84419c62 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -12,20 +12,16 @@ //! initialization and can otherwise silence errors, if //! move analysis runs after promotion on broken MIR. -use rustc_ast::LitKind; use rustc_hir as hir; -use rustc_hir::def_id::DefId; use rustc_middle::mir::traversal::ReversePostorder; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{self, List, TyCtxt, TypeFoldable}; -use rustc_span::symbol::sym; use rustc_span::Span; use rustc_index::vec::{Idx, IndexVec}; -use rustc_target::spec::abi::Abi; use std::cell::Cell; use std::{cmp, iter, mem}; @@ -36,8 +32,8 @@ /// A `MirPass` for promotion. /// -/// Promotion is the extraction of promotable temps into separate MIR bodies. This pass also emits -/// errors when promotion of `#[rustc_args_required_const]` arguments fails. +/// Promotion is the extraction of promotable temps into separate MIR bodies so they can have +/// `'static` lifetime. /// /// After this pass is run, `promoted_fragments` will hold the MIR body corresponding to each /// newly created `Constant`. @@ -101,47 +97,16 @@ pub fn is_promotable(&self) -> bool { pub enum Candidate { /// Borrow of a constant temporary, candidate for lifetime extension. Ref(Location), - - /// Currently applied to function calls where the callee has the unstable - /// `#[rustc_args_required_const]` attribute as well as the SIMD shuffle - /// intrinsic. The intrinsic requires the arguments are indeed constant and - /// the attribute currently provides the semantic requirement that arguments - /// must be constant. - 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(_) => false, - Candidate::Argument { .. } => true, - } - } - fn source_info(&self, body: &Body<'_>) -> SourceInfo { match self { Candidate::Ref(location) => *body.source_info(*location), - Candidate::Argument { bb, .. } => *body.source_info(body.terminator_loc(*bb)), } } } -fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { - let attrs = tcx.get_attrs(def_id); - let attr = attrs.iter().find(|a| tcx.sess.check_name(a, sym::rustc_args_required_const))?; - let mut ret = vec![]; - for meta in attr.meta_item_list()? { - match meta.literal()?.kind { - LitKind::Int(a, _) => { - ret.push(a as usize); - } - _ => bug!("invalid arg index"), - } - } - Some(ret) -} - struct Collector<'a, 'tcx> { ccx: &'a ConstCx<'a, 'tcx>, temps: IndexVec, @@ -208,31 +173,6 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { _ => {} } } - - fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { - self.super_terminator(terminator, location); - - if let TerminatorKind::Call { ref func, .. } = terminator.kind { - if let ty::FnDef(def_id, _) = *func.ty(self.ccx.body, self.ccx.tcx).kind() { - let fn_sig = self.ccx.tcx.fn_sig(def_id); - if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() { - let name = self.ccx.tcx.item_name(def_id); - // FIXME(eddyb) use `#[rustc_args_required_const(2)]` for shuffles. - if name.as_str().starts_with("simd_shuffle") { - self.candidates.push(Candidate::Argument { bb: location.block, index: 2 }); - - return; // Don't double count `simd_shuffle` candidates - } - } - - if let Some(constant_args) = args_required_const(self.ccx.tcx, def_id) { - for index in constant_args { - self.candidates.push(Candidate::Argument { bb: location.block, index }); - } - } - } - } - } } pub fn collect_temps_and_candidates( @@ -256,14 +196,6 @@ pub fn collect_temps_and_candidates( struct Validator<'a, 'tcx> { ccx: &'a ConstCx<'a, 'tcx>, temps: &'a IndexVec, - - /// 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` - /// 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. - explicit: bool, } impl std::ops::Deref for Validator<'a, 'tcx> { @@ -280,8 +212,6 @@ impl<'tcx> Validator<'_, 'tcx> { fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> { match candidate { Candidate::Ref(loc) => { - assert!(!self.explicit); - let statement = &self.body[loc.block].statements[loc.statement_index]; match &statement.kind { StatementKind::Assign(box (_, Rvalue::Ref(_, kind, place))) => { @@ -310,15 +240,6 @@ fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> { _ => bug!(), } } - Candidate::Argument { bb, index } => { - assert!(self.explicit); - - let terminator = self.body[bb].terminator(); - match &terminator.kind { - TerminatorKind::Call { args, .. } => self.validate_operand(&args[index]), - _ => bug!(), - } - } } } @@ -448,12 +369,10 @@ fn validate_place(&self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> { ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {} ProjectionElem::Index(local) => { - if !self.explicit { - let mut promotable = false; - // Only accept if we can predict the index and are indexing an array. - let val = if let TempState::Defined { location: loc, .. } = - self.temps[local] - { + let mut promotable = false; + // Only accept if we can predict the index and are indexing an array. + let val = + if let TempState::Defined { location: loc, .. } = self.temps[local] { let block = &self.body[loc.block]; if loc.statement_index < block.statements.len() { let statement = &block.statements[loc.statement_index]; @@ -470,38 +389,35 @@ fn validate_place(&self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> { } else { None }; - if let Some(idx) = val { - // Determine the type of the thing we are indexing. - let ty = place_base.ty(self.body, self.tcx).ty; - match ty.kind() { - ty::Array(_, len) => { - // It's an array; determine its length. - if let Some(len) = - len.try_eval_usize(self.tcx, self.param_env) - { - // If the index is in-bounds, go ahead. - if idx < len { - promotable = true; - } + if let Some(idx) = val { + // Determine the type of the thing we are indexing. + let ty = place_base.ty(self.body, self.tcx).ty; + match ty.kind() { + ty::Array(_, len) => { + // It's an array; determine its length. + if let Some(len) = len.try_eval_usize(self.tcx, self.param_env) + { + // If the index is in-bounds, go ahead. + if idx < len { + promotable = true; } } - _ => {} } - } - if !promotable { - return Err(Unpromotable); + _ => {} } } + if !promotable { + return Err(Unpromotable); + } + self.validate_local(local)?; } ProjectionElem::Field(..) => { let base_ty = place_base.ty(self.body, self.tcx).ty; - if let Some(def) = base_ty.ty_adt_def() { + if base_ty.is_union() { // No promotion of union field accesses. - if def.is_union() { - return Err(Unpromotable); - } + return Err(Unpromotable); } } } @@ -636,7 +552,7 @@ fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> { match op { BinOp::Div | BinOp::Rem => { - if !self.explicit && lhs_ty.is_integral() { + if lhs_ty.is_integral() { // Integer division: the RHS must be a non-zero const. let const_val = match rhs { Operand::Constant(c) => { @@ -721,13 +637,12 @@ fn validate_call( ) -> Result<(), Unpromotable> { let fn_ty = callee.ty(self.body, self.tcx); - // When doing explicit promotion and inside const/static items, we promote all (eligible) function calls. + // Inside const/static items, we promote all (eligible) function calls. // Everywhere else, we require `#[rustc_promotable]` on the callee. - let promote_all_const_fn = self.explicit - || matches!( - self.const_kind, - Some(hir::ConstContext::Static(_) | hir::ConstContext::Const) - ); + let promote_all_const_fn = matches!( + self.const_kind, + Some(hir::ConstContext::Static(_) | hir::ConstContext::Const) + ); if !promote_all_const_fn { if let ty::FnDef(def_id, _) = *fn_ty.kind() { // Never promote runtime `const fn` calls of @@ -765,41 +680,12 @@ pub fn validate_candidates( temps: &IndexVec, candidates: &[Candidate], ) -> Vec { - let mut validator = Validator { ccx, temps, explicit: false }; + let validator = Validator { ccx, temps }; candidates .iter() .copied() - .filter(|&candidate| { - validator.explicit = candidate.forces_explicit_promotion(); - - // FIXME(eddyb) also emit the errors for shuffle indices - // and `#[rustc_args_required_const]` arguments here. - - let is_promotable = validator.validate_candidate(candidate).is_ok(); - - // If we use explicit validation, we carry the risk of turning a legitimate run-time - // operation into a failing compile-time operation. Make sure that does not happen - // by asserting that there is no possible run-time behavior here in case promotion - // fails. - if validator.explicit && !is_promotable { - ccx.tcx.sess.delay_span_bug( - ccx.body.span, - "Explicit promotion requested, but failed to promote", - ); - } - - match candidate { - Candidate::Argument { bb, index } if !is_promotable => { - let span = ccx.body[bb].terminator().source_info.span; - let msg = format!("argument {} is required to be a constant", index + 1); - ccx.tcx.sess.span_err(span, &msg); - } - _ => (), - } - - is_promotable - }) + .filter(|&candidate| validator.validate_candidate(candidate).is_ok()) .collect() } @@ -1039,26 +925,6 @@ fn promote_candidate( _ => bug!(), } } - Candidate::Argument { bb, index } => { - let terminator = blocks[bb].terminator_mut(); - match terminator.kind { - TerminatorKind::Call { ref mut args, .. } => { - let ty = args[index].ty(local_decls, self.tcx); - let span = terminator.source_info.span; - - Rvalue::Use(mem::replace(&mut args[index], promoted_operand(ty, span))) - } - // We expected a `TerminatorKind::Call` for which we'd like to promote an - // argument. `qualify_consts` saw a `TerminatorKind::Call` here, but - // we are seeing a `Goto`. That means that the `promote_temps` method - // already promoted this call away entirely. This case occurs when calling - // a function requiring a constant argument and as that constant value - // providing a value whose computation contains another call to a function - // requiring a constant argument. - TerminatorKind::Goto { .. } => return None, - _ => bug!(), - } - } } }; @@ -1113,7 +979,6 @@ pub fn promote_candidates<'tcx>( } } } - Candidate::Argument { .. } => {} } // Declare return place local so that `mir::Body::new` doesn't complain.