Skip to content

Commit

Permalink
Add support for declaring 'const fn'
Browse files Browse the repository at this point in the history
Add a new feature to enable this, since `const extern fn`
support is unstable
  • Loading branch information
Aaron1011 committed Oct 28, 2019
1 parent 53bdffc commit 5dfc2c8
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 12 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ std = []
align = []
rustc-dep-of-std = ['align', 'rustc-std-workspace-core']
extra_traits = []
const-extern-fn = []
# use_std is deprecated, use `std` instead
use_std = [ 'std' ]

Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ libc = "0.2"
* `extra_traits`: all `struct`s implemented in `libc` are `Copy` and `Clone`.
This feature derives `Debug`, `Eq`, `Hash`, and `PartialEq`.

* `const-extern-fn`: Changes some `extern fn`s into `const extern fn`s.
This features requires a nightly rustc

* **deprecated**: `use_std` is deprecated, and is equivalent to `std`.

## Rust version support
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#![no_std]
#![cfg_attr(feature = "rustc-dep-of-std", no_core)]
#![cfg_attr(target_os = "redox", feature(static_nobundle))]
#![cfg_attr(feature = "const-extern-fn", feature(const_extern_fn))]

#[macro_use]
mod macros;
Expand Down
98 changes: 89 additions & 9 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,96 @@ macro_rules! s_no_extra_traits {
);
}

#[allow(unused_macros)]
macro_rules! f {
($(pub fn $i:ident($($arg:ident: $argty:ty),*) -> $ret:ty {
$($body:stmt);*
})*) => ($(
#[inline]
pub unsafe extern fn $i($($arg: $argty),*) -> $ret {
$($body);*
// This is a pretty horrible hack to allow us to conditionally mark
// some functions as 'const', without requiring users of this macro
// to care about the "const-extern-fn" feature.
//
// When 'const-extern-fn' is enabled, we emit the captured 'const' keyword
// in the expanded function.
//
// When 'const-extern-fn' is disabled, we always emit a plain 'pub unsafe extern fn'.
// Note that the expression matched by the macro is exactly the same - this allows
// users of this macro to work whether or not 'const-extern-fn' is enabled
//
// Unfortunately, we need to duplicate most of this macro between the 'cfg_if' blocks.
// This is because 'const unsafe extern fn' won't even parse on older compilers,
// so we need to avoid emitting it at all of 'const-extern-fn'.
//
// Specifically, moving the 'cfg_if' into the macro body will *not* work.
// Doing so would cause the '#[cfg(feature = "const-extern-fn")]' to be emiited
// into user code. The 'cfg' gate will not stop Rust from trying to parse the
// 'pub const unsafe extern fn', so users would get a compiler error even when
// the 'const-extern-fn' feature is disabled
//
// Note that users of this macro need to place 'const' in a weird position
// (after the closing ')' for the arguments, but before the return type).
// This was the only way I could satisfy the following two requirements:
// 1. Avoid ambuguity errors from 'macro_rules!' (which happen when writing '$foo:ident fn'
// 2. Allow users of this macro to mix 'pub fn foo' and 'pub const fn bar' within the same
// 'f!' block
cfg_if! {
if #[cfg(feature = "const-extern-fn")] {
#[allow(unused_macros)]
macro_rules! f {
($(pub $({$constness:ident})* fn $i:ident(
$($arg:ident: $argty:ty),*
) -> $ret:ty {
$($body:stmt);*
})*) => ($(
#[inline]
pub $($constness)* unsafe extern fn $i($($arg: $argty),*
) -> $ret {
$($body);*
}
)*)
}
)*)

#[allow(unused_macros)]
macro_rules! const_fn {
($($({$constness:ident})* fn $i:ident(
$($arg:ident: $argty:ty),*
) -> $ret:ty {
$($body:stmt);*
})*) => ($(
#[inline]
$($constness)* fn $i($($arg: $argty),*
) -> $ret {
$($body);*
}
)*)
}

} else {
#[allow(unused_macros)]
macro_rules! f {
($(pub $({$constness:ident})* fn $i:ident(
$($arg:ident: $argty:ty),*
) -> $ret:ty {
$($body:stmt);*
})*) => ($(
#[inline]
pub unsafe extern fn $i($($arg: $argty),*
) -> $ret {
$($body);*
}
)*)
}

#[allow(unused_macros)]
macro_rules! const_fn {
($($({$constness:ident})* fn $i:ident(
$($arg:ident: $argty:ty),*
) -> $ret:ty {
$($body:stmt);*
})*) => ($(
#[inline]
fn $i($($arg: $argty),*
) -> $ret {
$($body);*
}
)*)
}
}
}

#[allow(unused_macros)]
Expand Down
8 changes: 5 additions & 3 deletions src/unix/linux_like/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1165,8 +1165,10 @@ pub const ARPHRD_IEEE802154: u16 = 804;
pub const ARPHRD_VOID: u16 = 0xFFFF;
pub const ARPHRD_NONE: u16 = 0xFFFE;

fn CMSG_ALIGN(len: usize) -> usize {
len + ::mem::size_of::<usize>() - 1 & !(::mem::size_of::<usize>() - 1)
const_fn! {
{const} fn CMSG_ALIGN(len: usize) -> usize {
len + ::mem::size_of::<usize>() - 1 & !(::mem::size_of::<usize>() - 1)
}
}

f! {
Expand All @@ -1182,7 +1184,7 @@ f! {
cmsg.offset(1) as *mut ::c_uchar
}

pub fn CMSG_SPACE(length: ::c_uint) -> ::c_uint {
pub {const} fn CMSG_SPACE(length: ::c_uint) -> ::c_uint {
(CMSG_ALIGN(length as usize) + CMSG_ALIGN(::mem::size_of::<cmsghdr>()))
as ::c_uint
}
Expand Down

0 comments on commit 5dfc2c8

Please sign in to comment.