Skip to content

Commit

Permalink
Rollup merge of rust-lang#48018 - alexcrichton:require-const-arg, r=e…
Browse files Browse the repository at this point in the history
…ddyb

rustc: Add `#[rustc_args_required_const]`

This commit adds a new unstable attribute to the compiler which requires that
arguments to a function are always provided as constants. The primary use case
for this is SIMD intrinsics where arguments are defined by vendors to be
constant and in LLVM they indeed must be constant as well.

For now this is mostly just a semantic guarantee in rustc that an argument is a
constant when invoked, phases like trans don't actually take advantage of it
yet. This means that we'll be able to use this in stdsimd but we won't be able
to remove the `constify_*` macros just yet. Hopefully soon though!
  • Loading branch information
Manishearth authored Feb 7, 2018
2 parents 6908fb7 + 27a4e73 commit 993322e
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 11 deletions.
19 changes: 11 additions & 8 deletions src/librustc_mir/transform/promote_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,12 @@ pub enum Candidate {
/// Borrow of a constant temporary.
Ref(Location),

/// Array of indices found in the third argument of
/// a call to one of the simd_shuffleN intrinsics.
ShuffleIndices(BasicBlock)
/// 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 },
}

struct TempCollector<'tcx> {
Expand Down Expand Up @@ -303,10 +306,10 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
_ => bug!()
}
}
Candidate::ShuffleIndices(bb) => {
Candidate::Argument { bb, index } => {
match self.source[bb].terminator_mut().kind {
TerminatorKind::Call { ref mut args, .. } => {
Rvalue::Use(mem::replace(&mut args[2], new_operand))
Rvalue::Use(mem::replace(&mut args[index], new_operand))
}
_ => bug!()
}
Expand Down Expand Up @@ -359,15 +362,15 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
}
(statement.source_info.span, dest.ty(mir, tcx).to_ty(tcx))
}
Candidate::ShuffleIndices(bb) => {
Candidate::Argument { bb, index } => {
let terminator = mir[bb].terminator();
let ty = match terminator.kind {
TerminatorKind::Call { ref args, .. } => {
args[2].ty(mir, tcx)
args[index].ty(mir, tcx)
}
_ => {
span_bug!(terminator.source_info.span,
"expected simd_shuffleN call to promote");
"expected call argument to promote");
}
};
(terminator.source_info.span, ty)
Expand Down
45 changes: 42 additions & 3 deletions src/librustc_mir/transform/qualify_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use rustc_data_structures::bitvec::BitVector;
use rustc_data_structures::indexed_set::IdxSetBuf;
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
use rustc_data_structures::fx::FxHashSet;
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::middle::const_val::ConstVal;
Expand All @@ -30,6 +31,7 @@ use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::middle::lang_items;
use syntax::abi::Abi;
use syntax::attr;
use syntax::ast::LitKind;
use syntax::feature_gate::UnstableFeatures;
use syntax_pos::{Span, DUMMY_SP};

Expand Down Expand Up @@ -407,7 +409,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
_ => {}
}
}
Candidate::ShuffleIndices(_) => {}
Candidate::Argument { .. } => {}
}
}

Expand Down Expand Up @@ -730,8 +732,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
self.visit_operand(func, location);

let fn_ty = func.ty(self.mir, self.tcx);
let mut callee_def_id = None;
let (mut is_shuffle, mut is_const_fn) = (false, None);
if let ty::TyFnDef(def_id, _) = fn_ty.sty {
callee_def_id = Some(def_id);
match self.tcx.fn_sig(def_id).abi() {
Abi::RustIntrinsic |
Abi::PlatformIntrinsic => {
Expand All @@ -754,17 +758,39 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
}
}

let constant_arguments = callee_def_id.and_then(|id| {
args_required_const(self.tcx, id)
});
for (i, arg) in args.iter().enumerate() {
self.nest(|this| {
this.visit_operand(arg, location);
if is_shuffle && i == 2 && this.mode == Mode::Fn {
let candidate = Candidate::ShuffleIndices(bb);
if this.mode != Mode::Fn {
return
}
let candidate = Candidate::Argument { bb, index: i };
if is_shuffle && i == 2 {
if this.can_promote() {
this.promotion_candidates.push(candidate);
} else {
span_err!(this.tcx.sess, this.span, E0526,
"shuffle indices are not constant");
}
return
}

let constant_arguments = match constant_arguments.as_ref() {
Some(s) => s,
None => return,
};
if !constant_arguments.contains(&i) {
return
}
if this.can_promote() {
this.promotion_candidates.push(candidate);
} else {
this.tcx.sess.span_err(this.span,
&format!("argument {} is required to be a constant",
i + 1));
}
});
}
Expand Down Expand Up @@ -1085,3 +1111,16 @@ impl MirPass for QualifyAndPromoteConstants {
}
}
}

fn args_required_const(tcx: TyCtxt, def_id: DefId) -> Option<FxHashSet<usize>> {
let attrs = tcx.get_attrs(def_id);
let attr = attrs.iter().find(|a| a.check_name("rustc_args_required_const"))?;
let mut ret = FxHashSet();
for meta in attr.meta_item_list()? {
match meta.literal()?.node {
LitKind::Int(a, _) => { ret.insert(a as usize); }
_ => return None,
}
}
Some(ret)
}
36 changes: 36 additions & 0 deletions src/test/compile-fail/rustc-args-required-const.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(attr_literals, rustc_attrs, const_fn)]

#[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);
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
}

0 comments on commit 993322e

Please sign in to comment.