about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2018-02-05 10:48:22 -0800
committerAlex Crichton <alex@alexcrichton.com>2018-02-05 10:58:13 -0800
commit27a4e73ca54f454d16cab7942ef9b27d5c942a32 (patch)
tree9e7c761d910fb335b5307e6cd8c22ea40c8f61fe
parentb0a396bb0af5ad7706ffa2fafdec761012df200d (diff)
downloadrust-27a4e73ca54f454d16cab7942ef9b27d5c942a32.tar.gz
rust-27a4e73ca54f454d16cab7942ef9b27d5c942a32.zip
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!
-rw-r--r--src/librustc_mir/transform/promote_consts.rs19
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs45
-rw-r--r--src/test/compile-fail/rustc-args-required-const.rs36
3 files changed, 89 insertions, 11 deletions
diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs
index 1545040f2da..b732eeb624c 100644
--- a/src/librustc_mir/transform/promote_consts.rs
+++ b/src/librustc_mir/transform/promote_consts.rs
@@ -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> {
@@ -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!()
                 }
@@ -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)
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index da76adfd48f..297e0e491f6 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -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;
@@ -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};
 
@@ -407,7 +409,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                         _ => {}
                     }
                 }
-                Candidate::ShuffleIndices(_) => {}
+                Candidate::Argument { .. } => {}
             }
         }
 
@@ -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 => {
@@ -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));
                     }
                 });
             }
@@ -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)
+}
diff --git a/src/test/compile-fail/rustc-args-required-const.rs b/src/test/compile-fail/rustc-args-required-const.rs
new file mode 100644
index 00000000000..aac9299eaaf
--- /dev/null
+++ b/src/test/compile-fail/rustc-args-required-const.rs
@@ -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
+}