From 35dca7edd3e5181459c8e410a59a1b6e3e97a360 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 8 Feb 2018 12:48:25 -0800 Subject: [PATCH 1/2] Add `rustc_args_required_const` to the feature whitelist Unfortunately left out it means that when the `#![feature(proc_macro)]` flag is in effect it fails to find `rustc_args_required_const` for expansion. This version, however, is verified to work with stdsimd's requirements! --- src/libsyntax/feature_gate.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 9c6520cd874a8..ae0556320b0ef 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -984,6 +984,11 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG "wasm_import_memory attribute is currently unstable", cfg_fn!(wasm_import_memory))), + ("rustc_args_required_const", Whitelisted, Gated(Stability::Unstable, + "rustc_attrs", + "never will be stable", + cfg_fn!(rustc_attrs))), + // Crate level attributes ("crate_name", CrateLevel, Ungated), ("crate_type", CrateLevel, Ungated), From 7a20fc14ef255df1ce2c417943605015aba2b1ff Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 8 Feb 2018 13:11:13 -0800 Subject: [PATCH 2/2] Disallow function pointers to #[rustc_args_required_const] This commit disallows acquiring a function pointer to functions tagged as `#[rustc_args_required_const]`. This is intended to be used as future-proofing for the stdsimd crate to avoid taking a function pointer to any intrinsic which has a hard requirement that one of the arguments is a constant value. --- src/librustc_mir/interpret/eval_context.rs | 4 +++ src/librustc_trans/mir/constant.rs | 4 +++ src/librustc_trans/mir/rvalue.rs | 4 +++ src/librustc_typeck/check/mod.rs | 32 +++++++++++++++++++ .../rustc-args-required-const2.rs | 20 ++++++++++++ 5 files changed, 64 insertions(+) create mode 100644 src/test/compile-fail/rustc-args-required-const2.rs diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 02fcb69fef5ac..52b87282180c4 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -716,6 +716,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> { ReifyFnPointer => { match self.eval_operand(operand)?.ty.sty { ty::TyFnDef(def_id, substs) => { + if self.tcx.has_attr(def_id, "rustc_args_required_const") { + bug!("reifying a fn ptr that requires \ + const arguments"); + } let instance = self.resolve(def_id, substs)?; let fn_ptr = self.memory.create_fn_alloc(instance); let valty = ValTy { diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index cd1975488a24a..ff9ea40073fce 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -714,6 +714,10 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { mir::CastKind::ReifyFnPointer => { match operand.ty.sty { ty::TyFnDef(def_id, substs) => { + if tcx.has_attr(def_id, "rustc_args_required_const") { + bug!("reifying a fn ptr that requires \ + const arguments"); + } callee::resolve_and_get_fn(self.cx, def_id, substs) } _ => { diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index d1bc4fe90014c..2e876ec118d57 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -195,6 +195,10 @@ impl<'a, 'tcx> FunctionCx<'a, 'tcx> { mir::CastKind::ReifyFnPointer => { match operand.layout.ty.sty { ty::TyFnDef(def_id, substs) => { + if bx.cx.tcx.has_attr(def_id, "rustc_args_required_const") { + bug!("reifying a fn ptr that requires \ + const arguments"); + } OperandValue::Immediate( callee::resolve_and_get_fn(bx.cx, def_id, substs)) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f044b2c711e20..8bb38332d0e67 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4877,6 +4877,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } + self.check_rustc_args_require_const(def.def_id(), node_id, span); + debug!("instantiate_value_path: type of {:?} is {:?}", node_id, ty_substituted); @@ -4884,6 +4886,36 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ty_substituted } + fn check_rustc_args_require_const(&self, + def_id: DefId, + node_id: ast::NodeId, + span: Span) { + // We're only interested in functions tagged with + // #[rustc_args_required_const], so ignore anything that's not. + if !self.tcx.has_attr(def_id, "rustc_args_required_const") { + return + } + + // If our calling expression is indeed the function itself, we're good! + // If not, generate an error that this can only be called directly. + match self.tcx.hir.get(self.tcx.hir.get_parent_node(node_id)) { + Node::NodeExpr(expr) => { + match expr.node { + hir::ExprCall(ref callee, ..) => { + if callee.id == node_id { + return + } + } + _ => {} + } + } + _ => {} + } + + self.tcx.sess.span_err(span, "this function can only be invoked \ + directly, not through a function pointer"); + } + /// Report errors if the provided parameters are too few or too many. fn check_path_parameter_count(&self, span: Span, diff --git a/src/test/compile-fail/rustc-args-required-const2.rs b/src/test/compile-fail/rustc-args-required-const2.rs new file mode 100644 index 0000000000000..aa63019307b5b --- /dev/null +++ b/src/test/compile-fail/rustc-args-required-const2.rs @@ -0,0 +1,20 @@ +// 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) { +} + +fn main() { + let a = foo; //~ ERROR: this function can only be invoked directly + a(2); +}