about summary refs log tree commit diff
diff options
context:
space:
mode:
-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
+}