Implement #[ffi_const] and #[ffi_pure] function attributes

Introduce function attribute corresponding to the `const`/`pure`
attributes supported by GCC, clang and other compilers.

Based on the work of gnzlbg <gonzalobg88@gmail.com>.
This commit is contained in:
Matthias Schiffer 2020-04-14 00:19:46 +02:00
parent 3a7dfda40a
commit abc236414b
7 changed files with 62 additions and 0 deletions

View File

@ -284,6 +284,12 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_RETURNS_TWICE) {
Attribute::ReturnsTwice.apply_llfn(Function, llfn);
}
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_PURE) {
Attribute::ReadOnly.apply_llfn(Function, llfn);
}
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::FFI_CONST) {
Attribute::ReadNone.apply_llfn(Function, llfn);
}
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
naked(llfn, true);
}

View File

@ -616,4 +616,7 @@
E0724, // `#[ffi_returns_twice]` is only allowed in foreign functions
E0726, // non-explicit (not `'_`) elided lifetime in unsupported position
// E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
E0755, // `#[ffi_pure]` is only allowed on foreign functions
E0756, // `#[ffi_const]` is only allowed on foreign functions
E0757, // `#[ffi_const]` functions cannot be `#[ffi_pure]`
}

View File

@ -565,6 +565,12 @@ pub fn set(&self, features: &mut Features, span: Span) {
/// Allow conditional compilation depending on rust version
(active, cfg_version, "1.45.0", Some(64796), None),
/// Allows the use of `#[ffi_pure]` on foreign functions.
(active, ffi_pure, "1.45.0", Some(58329), None),
/// Allows the use of `#[ffi_const]` on foreign functions.
(active, ffi_const, "1.45.0", Some(58328), None),
// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------

View File

@ -331,6 +331,8 @@ macro_rules! experimental {
),
gated!(ffi_returns_twice, Whitelisted, template!(Word), experimental!(ffi_returns_twice)),
gated!(ffi_pure, Whitelisted, template!(Word), experimental!(ffi_pure)),
gated!(ffi_const, Whitelisted, template!(Word), experimental!(ffi_const)),
gated!(track_caller, Whitelisted, template!(Word), experimental!(track_caller)),
gated!(
register_attr, CrateLevel, template!(List: "attr1, attr2, ..."),

View File

@ -77,6 +77,12 @@ pub struct CodegenFnAttrFlags: u32 {
const NO_SANITIZE_THREAD = 1 << 14;
/// All `#[no_sanitize(...)]` attributes.
const NO_SANITIZE_ANY = Self::NO_SANITIZE_ADDRESS.bits | Self::NO_SANITIZE_MEMORY.bits | Self::NO_SANITIZE_THREAD.bits;
/// #[ffi_pure]: applies clang's `pure` attribute to a foreign function
/// declaration.
const FFI_PURE = 1 << 15;
/// #[ffi_const]: applies clang's `const` attribute to a foreign function
/// declaration.
const FFI_CONST = 1 << 16;
}
}

View File

@ -322,6 +322,8 @@
f32,
f64,
feature,
ffi_const,
ffi_pure,
ffi_returns_twice,
field,
field_init_shorthand,

View File

@ -2374,6 +2374,43 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
)
.emit();
}
} else if attr.check_name(sym::ffi_pure) {
if tcx.is_foreign_item(id) {
if attrs.iter().any(|a| a.check_name(sym::ffi_const)) {
// `#[ffi_const]` functions cannot be `#[ffi_pure]`
struct_span_err!(
tcx.sess,
attr.span,
E0757,
"`#[ffi_const]` function cannot be `#[ffi_pure]`"
)
.emit();
} else {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE;
}
} else {
// `#[ffi_pure]` is only allowed on foreign functions
struct_span_err!(
tcx.sess,
attr.span,
E0755,
"`#[ffi_pure]` may only be used on foreign functions"
)
.emit();
}
} else if attr.check_name(sym::ffi_const) {
if tcx.is_foreign_item(id) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST;
} else {
// `#[ffi_const]` is only allowed on foreign functions
struct_span_err!(
tcx.sess,
attr.span,
E0756,
"`#[ffi_const]` may only be used on foreign functions"
)
.emit();
}
} else if attr.check_name(sym::rustc_allocator_nounwind) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND;
} else if attr.check_name(sym::naked) {