rustc_args_required_const is no longer a promotion site
This commit is contained in:
parent
e1ff91f439
commit
22e1778ec0
@ -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};
|
||||
@ -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.
|
||||
|
@ -1,11 +0,0 @@
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[rustc_args_required_const(0)]
|
||||
fn foo(_imm8: i32) {}
|
||||
|
||||
fn bar() {
|
||||
let imm8 = 3;
|
||||
foo(imm8) //~ ERROR argument 1 is required to be a constant
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,8 +0,0 @@
|
||||
error: argument 1 is required to be a constant
|
||||
--> $DIR/const_arg_local.rs:8:5
|
||||
|
|
||||
LL | foo(imm8)
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -1,10 +0,0 @@
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[rustc_args_required_const(0)]
|
||||
fn foo(_imm8: i32) {}
|
||||
|
||||
fn bar() {
|
||||
foo(*&mut 42) //~ ERROR argument 1 is required to be a constant
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,8 +0,0 @@
|
||||
error: argument 1 is required to be a constant
|
||||
--> $DIR/const_arg_promotable.rs:7:5
|
||||
|
|
||||
LL | foo(*&mut 42)
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -1,18 +0,0 @@
|
||||
// This test is a regression test for a bug where we only checked function calls in no-const
|
||||
// functions for `rustc_args_required_const` arguments. This meant that even though `bar` needs its
|
||||
// argument to be const, inside a const fn (callable at runtime), the value for it may come from a
|
||||
// non-constant (namely an argument to the const fn).
|
||||
|
||||
#![feature(rustc_attrs)]
|
||||
const fn foo(a: i32) {
|
||||
bar(a); //~ ERROR argument 1 is required to be a constant
|
||||
}
|
||||
|
||||
#[rustc_args_required_const(0)]
|
||||
const fn bar(_: i32) {}
|
||||
|
||||
fn main() {
|
||||
// this function call will pass a runtime-value (number of program arguments) to `foo`, which
|
||||
// will in turn forward it to `bar`, which expects a compile-time argument
|
||||
foo(std::env::args().count() as i32);
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
error: argument 1 is required to be a constant
|
||||
--> $DIR/const_arg_promotable2.rs:8:5
|
||||
|
|
||||
LL | bar(a);
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -1,10 +0,0 @@
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[rustc_args_required_const(0)]
|
||||
fn foo(_imm8: i32) {}
|
||||
|
||||
fn bar(imm8: i32) {
|
||||
foo(imm8) //~ ERROR argument 1 is required to be a constant
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,8 +0,0 @@
|
||||
error: argument 1 is required to be a constant
|
||||
--> $DIR/const_arg_wrapper.rs:7:5
|
||||
|
|
||||
LL | foo(imm8)
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -1,27 +0,0 @@
|
||||
#![feature(rustc_attrs)]
|
||||
|
||||
#[rustc_args_required_const(0)]
|
||||
fn foo(_a: i32) {
|
||||
}
|
||||
|
||||
#[rustc_args_required_const(1)]
|
||||
fn bar(_a: i32, _b: i32) {
|
||||
}
|
||||
|
||||
const A: i32 = 3;
|
||||
|
||||
const fn baz() -> i32 {
|
||||
3
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo(2);
|
||||
foo(2 + 3);
|
||||
const BAZ: i32 = baz();
|
||||
foo(BAZ);
|
||||
let a = 4;
|
||||
foo(A);
|
||||
foo(a); //~ ERROR: argument 1 is required to be a constant
|
||||
bar(a, 3);
|
||||
bar(a, a); //~ ERROR: argument 2 is required to be a constant
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
error: argument 1 is required to be a constant
|
||||
--> $DIR/rustc-args-required-const.rs:24:5
|
||||
|
|
||||
LL | foo(a);
|
||||
| ^^^^^^
|
||||
|
||||
error: argument 2 is required to be a constant
|
||||
--> $DIR/rustc-args-required-const.rs:26:5
|
||||
|
|
||||
LL | bar(a, a);
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
Loading…
x
Reference in New Issue
Block a user