1
Fork 0

Auto merge of #85110 - RalfJung:no-rustc_args_required_const, r=oli-obk

Remove rustc_args_required_const attribute

Now that stdarch no longer needs it (thanks `@Amanieu!),` we can kill the `rustc_args_required_const` attribute. This means that lifetime extension of references to temporaries is the only remaining job that promotion is performing. :-)

r? `@oli-obk`
Fixes https://github.com/rust-lang/rust/issues/69493
This commit is contained in:
bors 2021-05-13 13:37:32 +00:00
commit d2df620789
33 changed files with 111 additions and 613 deletions

View file

@ -14,7 +14,6 @@ use rustc_middle::ty::cast::{CastTy, IntTy};
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
use rustc_span::source_map::{Span, DUMMY_SP};
use rustc_span::symbol::sym;
use rustc_target::abi::{Abi, Int, LayoutOf, Variants};
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
@ -187,9 +186,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::CastKind::Pointer(PointerCast::ReifyFnPointer) => {
match *operand.layout.ty.kind() {
ty::FnDef(def_id, substs) => {
if bx.cx().tcx().has_attr(def_id, sym::rustc_args_required_const) {
bug!("reifying a fn ptr that requires const arguments");
}
let instance = ty::Instance::resolve_for_fn_ptr(
bx.tcx(),
ty::ParamEnv::reveal_all(),

View file

@ -469,7 +469,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
// ==========================================================================
rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL),
rustc_attr!(rustc_args_required_const, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE),
rustc_attr!(rustc_legacy_const_generics, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE),
// ==========================================================================

View file

@ -7,7 +7,6 @@ use rustc_middle::mir::CastKind;
use rustc_middle::ty::adjustment::PointerCast;
use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut};
use rustc_span::symbol::sym;
use rustc_target::abi::{Integer, LayoutOf, Variants};
use super::{
@ -49,13 +48,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// All reifications must be monomorphic, bail out otherwise.
ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
if self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
span_bug!(
self.cur_span(),
"reifying a fn ptr that requires const arguments"
);
}
let instance = ty::Instance::resolve_for_fn_ptr(
*self.tcx,
self.param_env,

View file

@ -305,7 +305,6 @@ where
let base_intern_mode = match intern_kind {
InternKind::Static(mutbl) => InternMode::Static(mutbl),
// `Constant` includes array lengths.
// `Promoted` includes non-`Copy` array initializers and `rustc_args_required_const` arguments.
InternKind::Constant | InternKind::Promoted => InternMode::Const,
};

View file

@ -5,6 +5,7 @@ use rustc_middle::mir::*;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
pub struct LowerIntrinsics;
@ -119,6 +120,9 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
terminator.kind = TerminatorKind::Goto { target };
}
}
_ if intrinsic_name.as_str().starts_with("simd_shuffle") => {
validate_simd_shuffle(tcx, args, terminator.source_info.span);
}
_ => {}
}
}
@ -132,9 +136,19 @@ fn resolve_rust_intrinsic(
) -> Option<(Symbol, SubstsRef<'tcx>)> {
if let ty::FnDef(def_id, substs) = *func_ty.kind() {
let fn_sig = func_ty.fn_sig(tcx);
if fn_sig.abi() == Abi::RustIntrinsic {
if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() {
return Some((tcx.item_name(def_id), substs));
}
}
None
}
fn validate_simd_shuffle(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
match &args[2] {
Operand::Constant(_) => {} // all good
_ => {
let msg = format!("last argument of `simd_shuffle` is required to be a `const` item");
tcx.sess.span_err(span, &msg);
}
}
}

View file

@ -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 @@ use crate::transform::MirPass;
/// 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 @@ impl TempState {
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<Vec<usize>> {
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<Local, TempState>,
@ -208,31 +173,6 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
_ => {}
}
}
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<Local, TempState>,
/// 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 @@ impl<'tcx> Validator<'_, 'tcx> {
_ => 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 @@ impl<'tcx> Validator<'_, 'tcx> {
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,28 +389,27 @@ impl<'tcx> Validator<'_, 'tcx> {
} 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)?;
}
@ -636,7 +554,7 @@ impl<'tcx> Validator<'_, 'tcx> {
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 +639,12 @@ impl<'tcx> Validator<'_, 'tcx> {
) -> 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 +682,12 @@ pub fn validate_candidates(
temps: &IndexVec<Local, TempState>,
candidates: &[Candidate],
) -> Vec<Candidate> {
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 +927,6 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
_ => 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 +981,6 @@ pub fn promote_candidates<'tcx>(
}
}
}
Candidate::Argument { .. } => {}
}
// Declare return place local so that `mir::Body::new` doesn't complain.

View file

@ -13,9 +13,7 @@ use rustc_errors::{pluralize, struct_span_err, Applicability};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{
self, FnSig, ForeignItem, ForeignItemKind, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID,
};
use rustc_hir::{self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID};
use rustc_hir::{MethodKind, Target};
use rustc_session::lint::builtin::{
CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES,
@ -81,9 +79,6 @@ impl CheckAttrVisitor<'tcx> {
sym::doc => self.check_doc_attrs(attr, hir_id, target, &mut specified_inline),
sym::no_link => self.check_no_link(hir_id, &attr, span, target),
sym::export_name => self.check_export_name(hir_id, &attr, span, target),
sym::rustc_args_required_const => {
self.check_rustc_args_required_const(&attr, span, target, item)
}
sym::rustc_layout_scalar_valid_range_start
| sym::rustc_layout_scalar_valid_range_end => {
self.check_rustc_layout_scalar_valid_range(&attr, span, target)
@ -948,79 +943,6 @@ impl CheckAttrVisitor<'tcx> {
}
}
/// Checks if `#[rustc_args_required_const]` is applied to a function and has a valid argument.
fn check_rustc_args_required_const(
&self,
attr: &Attribute,
span: &Span,
target: Target,
item: Option<ItemLike<'_>>,
) -> bool {
let is_function = matches!(target, Target::Fn | Target::Method(..) | Target::ForeignFn);
if !is_function {
self.tcx
.sess
.struct_span_err(attr.span, "attribute should be applied to a function")
.span_label(*span, "not a function")
.emit();
return false;
}
let list = match attr.meta_item_list() {
// The attribute form is validated on AST.
None => return false,
Some(it) => it,
};
let mut invalid_args = vec![];
for meta in list {
if let Some(LitKind::Int(val, _)) = meta.literal().map(|lit| &lit.kind) {
if let Some(ItemLike::Item(Item {
kind: ItemKind::Fn(FnSig { decl, .. }, ..),
..
}))
| Some(ItemLike::ForeignItem(ForeignItem {
kind: ForeignItemKind::Fn(decl, ..),
..
})) = item
{
let arg_count = decl.inputs.len() as u128;
if *val >= arg_count {
let span = meta.span();
self.tcx
.sess
.struct_span_err(span, "index exceeds number of arguments")
.span_label(
span,
format!(
"there {} only {} argument{}",
if arg_count != 1 { "are" } else { "is" },
arg_count,
pluralize!(arg_count)
),
)
.emit();
return false;
}
} else {
bug!("should be a function item");
}
} else {
invalid_args.push(meta.span());
}
}
if !invalid_args.is_empty() {
self.tcx
.sess
.struct_span_err(invalid_args, "arguments should be non-negative integers")
.emit();
false
} else {
true
}
}
fn check_rustc_layout_scalar_valid_range(
&self,
attr: &Attribute,

View file

@ -994,7 +994,6 @@ symbols! {
rustc_allocator,
rustc_allocator_nounwind,
rustc_allow_const_fn_unstable,
rustc_args_required_const,
rustc_attrs,
rustc_builtin_macro,
rustc_capture_analysis,

View file

@ -1537,8 +1537,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
self.check_rustc_args_require_const(def_id, hir_id, span);
debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted);
self.write_substs(hir_id, substs);

View file

@ -18,7 +18,7 @@ use rustc_middle::ty::adjustment::AllowTwoPhase;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::{self, Ty};
use rustc_session::Session;
use rustc_span::symbol::{sym, Ident};
use rustc_span::symbol::Ident;
use rustc_span::{self, MultiSpan, Span};
use rustc_trait_selection::traits::{self, ObligationCauseCode, StatementAsExpression};
@ -720,34 +720,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty
}
pub(in super::super) fn check_rustc_args_require_const(
&self,
def_id: DefId,
hir_id: hir::HirId,
span: Span,
) {
// We're only interested in functions tagged with
// #[rustc_args_required_const], so ignore anything that's not.
if !self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
return;
}
// If our calling expression is indeed the function itself, we're good!
// If not, generate an error that this can only be called directly.
if let Node::Expr(expr) = self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)) {
if let ExprKind::Call(ref callee, ..) = expr.kind {
if callee.hir_id == hir_id {
return;
}
}
}
self.tcx.sess.span_err(
span,
"this function can only be invoked directly, not through a function pointer",
);
}
/// A common error is to add an extra semicolon:
///
/// ```