Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for making functions const #1536

Merged
merged 6 commits into from
Nov 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
23 changes: 19 additions & 4 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ use std::process::Command;
use std::str;

fn main() {
let rustc_minor_ver =
rustc_minor_version().expect("Failed to get rustc version");
let (rustc_minor_ver, is_nightly) =
rustc_minor_nightly().expect("Failed to get rustc version");
let rustc_dep_of_std = env::var("CARGO_FEATURE_RUSTC_DEP_OF_STD").is_ok();
let align_cargo_feature = env::var("CARGO_FEATURE_ALIGN").is_ok();
let const_extern_fn_cargo_feature =
env::var("CARGO_FEATURE_CONST_EXTERN_FN").is_ok();
let libc_ci = env::var("LIBC_CI").is_ok();

if env::var("CARGO_FEATURE_USE_STD").is_ok() {
Expand Down Expand Up @@ -72,9 +74,16 @@ fn main() {
if rustc_dep_of_std {
println!("cargo:rustc-cfg=libc_thread_local");
}

if const_extern_fn_cargo_feature {
if !is_nightly || rustc_minor_ver < 40 {
panic!("const-extern-fn requires a nightly compiler >= 1.40")
}
println!("cargo:rustc-cfg=libc_const_extern_fn");
Aaron1011 marked this conversation as resolved.
Show resolved Hide resolved
}
}

fn rustc_minor_version() -> Option<u32> {
fn rustc_minor_nightly() -> Option<(u32, bool)> {
macro_rules! otry {
($e:expr) => {
match $e {
Expand All @@ -93,7 +102,13 @@ fn rustc_minor_version() -> Option<u32> {
return None;
}

otry!(pieces.next()).parse().ok()
let minor = pieces.next();
let nightly_raw = otry!(otry!(pieces.next()).split('-').nth(1));
let nightly =
nightly_raw.starts_with("dev") || nightly_raw.starts_with("nightly");
let minor = otry!(otry!(minor).parse().ok());

Some((minor, nightly))
}

fn which_freebsd() -> Option<i32> {
Expand Down
7 changes: 7 additions & 0 deletions ci/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ test_target() {
cargo "+${RUST}" "${BUILD_CMD}" -vv $opt --no-default-features --target "${TARGET}" \
--features extra_traits

# Test the 'const-extern-fn' feature on nightly
if [ "${RUST}" = "nightly" ]; then
cargo "+${RUST}" "${BUILD_CMD}" -vv $opt --no-default-features --target "${TARGET}" \
--features const-extern-fn
fi


# Also test that it builds with `extra_traits` and default features:
if [ "$NO_STD" != "1" ]; then
cargo "+${RUST}" "${BUILD_CMD}" -vv $opt --target "${TARGET}" \
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(libc_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
gnzlbg marked this conversation as resolved.
Show resolved Hide resolved
cfg_if! {
if #[cfg(libc_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
5 changes: 5 additions & 0 deletions tests/const_fn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#![cfg(libc_const_extern_fn)] // If this does not hold, the file is empty

#[cfg(target_os = "linux")]
const _FOO: libc::c_uint = unsafe { libc::CMSG_SPACE(1) };
//^ if CMSG_SPACE is not const, this will fail to compile