From 27a4e73ca54f454d16cab7942ef9b27d5c942a32 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 5 Feb 2018 10:48:22 -0800 Subject: [PATCH] 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! --- src/librustc_mir/transform/promote_consts.rs | 19 ++++---- src/librustc_mir/transform/qualify_consts.rs | 45 +++++++++++++++++-- .../compile-fail/rustc-args-required-const.rs | 36 +++++++++++++++ 3 files changed, 89 insertions(+), 11 deletions(-) create mode 100644 src/test/compile-fail/rustc-args-required-const.rs diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 1545040f2da79..b732eeb624c6d 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 da76adfd48f3f..297e0e491f694 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> { + 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 0000000000000..aac9299eaafb9 --- /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 or the MIT license +// , 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 +}