Given the following files:
main.rs:
mod ffi;
mod impl_do_print;
fn main() {
unsafe {
ffi::do_print(42.0);
}
}
ffi.rs:
extern "C" {
pub fn do_print(x: f32);
}
impl_do_print.rs:
#[no_mangle]
pub extern "C" fn do_print(x: i32) {
println!("{}", x);
}
Obviously, the f32 of the definition and the i32 of the implementation don't match.
When I execute this, it prints:
1047505936
I understand that no_mangle is automatically considered unsafe, but is there any way I could ask the compiler to catch the mismatch, or would I have to write my own linter for this?
Usecase:
This question came up with generated FFIs. I am able to modify the implementation in any way possible, but I cannot edit the function definition, as it is generated via bindgen.
So far, two potentially viable solutions were proposed:
Use the ffi.rs to auto-generate an implementation for the method that simply forwards the call to my own implementation. That way, the compiler would catch mismatching arguments.
Use the ffi.rs to generate a type definition for the function. That way, you could write a compiler check to verify that the implementation matches the definition, like this: const _: fn_type = fn_impl.
Either way, it seems to require proc macros or an external generator.
After a little more experimenting, I managed to achieve a compile time check by utilizing a macro:
impl_do_print.rs:
#[no_mangle]
pub extern "C" fn do_print(x: i32) {
println!("{}", x);
}
macro_rules! check_implementation_type {
($t:ty, $name:ident) => {
const _: $t = $name;
const _: $t = crate::ffi::$name;
};
}
check_implementation_type!(unsafe extern "C" fn(i32), do_print);
This still requires me to write a check_implementation_type entry for every function I implement, but it gives me a reliable compile time error if either the ffi.rs or the implementation don't match:
error[E0308]: mismatched types
--> src/impl_do_print.rs:9:23
|
9 | const _: $t = crate::ffi::$name;
| ^^^^^^^^^^^^^^^^^ expected `i32`, found `f32`
...
13 | check_implementation_type!(unsafe extern "C" fn(i32), do_print);
| --------------------------------------------------------------- in this macro invocation
|
= note: expected fn pointer `unsafe extern "C" fn(i32)`
found fn item `unsafe extern "C" fn(f32) {ffi::do_print}`
= note: this error originates in the macro `check_implementation_type` (in Nightly builds, run with -Z macro-backtrace for more info)
Related
I have a crate, ffizz-string, which has a number of public functions with an fz_string_ prefix. In another crate taskchampion-lib, I would like to export these same functions, but with a different prefix (tc_string_). The idea here is that ffizz-string provides some generic FFI string utilities, and the crate using those utilities wants to "rebrand" the functions to match the crate's other FFI functions.
For Rust libraries, this renaming is easy: pub use fz_string_borrow as tc_string_borrow. However, this does not seem to do the trick for exported functions. I have a few things I've tried below, and for each I looked at the resulting symbols using `nm ./target/debug/deps/libtaskchampion_lib-....rlib | grep string_borrow
#![allow(non_upper_case_globals)]
// attempt 0: demonstrate that regular function symbols appear in the nm grep
#[no_mangle]
pub unsafe extern "C" fn tc_string_borrow_0() {
todo!()
}
// result: appears in nm grep as 'tc_string_borrow_0`:
// 0000000000000000 T tc_string_borrow_0
// attempt 1: just rename using 'as'
pub use ffizz_string::fz_string_borrow as tc_string_borrow_1;
// result: appears in nm grep as 'fz_string_borrow`
// attempt 2: rename with `link_name`
// link_name only applies to foreign fns and statics
extern "C" {
#[link_name = "tc_string_borrow_2"]
pub static tc_string_borrow_2:
unsafe extern "C" fn(cstr: *const i8) -> ffizz_string::fz_string_t;
}
// result: not renamed (turns out link_name was the wrong attribute)
// attempt 3: export_name on a fn
/*
// "attribute should be applied to a free function, impl method or static"
#[export_name="tc_string_borrow_3"]
pub use ffizz_string::fz_string_borrow as tc_string_borrow_3;
*/
// result: error: attribute should be applied to a free function, impl method or static
// attempt 4: export_name on a static
#[export_name = "tc_string_borrow_4"]
pub static tc_string_borrow_4: unsafe extern "C" fn(cstr: *const i8) -> ffizz_string::fz_string_t =
ffizz_string::fz_string_borrow;
// result: compiles, but tc_string_borrow_4 is a pointer to fz_string_borrow:
// 0000000000000000 D tc_string_borrow_4
// attempt 5: export_name on a static
#[used]
pub static tc_string_borrow_5: unsafe extern "C" fn(cstr: *const i8) -> ffizz_string::fz_string_t =
ffizz_string::fz_string_borrow;
// result: not renamed
The one approach that I've found effective is to wrap the utility functions with another function:
#[inline]
pub unsafe extern "C" fn tc_string_borrow(cstr: *const i8) -> ffizz_string::fz_string_t {
ffizz_string::fz_string_borrow(cstr)
}
But this relies on the compiler to inline those cross-crate calls, and also requires reiterating the function signature. Is there a better way?
I have a code that looks approximately like this:
// src/bitboard.rs
#[derive(Copy, Clone, Debug)]
pub struct Bitboard {
value: u64
}
impl Bitboard {
pub const fn new(value: u64) -> Self {
Self { value }
}
pub const fn get_bit(&self, k: u64) -> u64 {
((1u64 << k) & self.value) >> k
}
}
// src/main.rs
pub mod bitboard;
use crate::bitboard::Bitboard;
fn main() {
let bitboard = Bitboard::new(0);
dbg!(bitboard);
}
If I compile it exactly like this, it works without any errors or warnings.
However, if I change my pub mod bitboard to mod bitboard, then clippy starts giving me this warning:
warning: this argument (8 byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte)
pub const fn get_bit(&self, k: u64) -> u64 {
^^^^^ help: consider passing by value instead: `self`
= note: `-W clippy::trivially-copy-pass-by-ref` implied by `-W clippy::pedantic`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
I understand what clippy is telling me to do. But I do not understand why it does not suggest this when my module is declared as public. Any ideas?
P.S.: I am using rustc 1.60.0, clippy 0.1.60.
Edit: I should also add that this is not the only extra lint that happens when I remove the pub from my modules. For instance, I also had 1 new clippy::upper_case_acronyms, and 1 new clippy::enum_variant_names.
Edit 2: As requested, I'm including more examples to show the same behaviour happening to clippy::upper-case_acronyms and clippy::enum_variant_names:
// src/fen.rs
pub struct FEN; // upper case acronyms happens here
pub enum FENValidationError { // enum variant names happens here
InvalidFieldsCount,
InvalidPiecePlacement,
InvalidRankSize,
}
// src/main.rs
mod fen; // same thing here. If this becomes `pub mod fen`, the two lint warnings above disappear.
Because changing a public API like that is a breaking change.
Changing the argument to pass by value like clippy recommends is easy if its an internal method, since you have control over all of the code that uses it. But if the argument is on a function or method exposed as part of the public API, changing it would require that all the other projects that use it change too, which is usually unacceptable for something as minor as passing a small Copy value by reference.
I'm implementing a crate that has the purpose of wrapping some user code and make sure it is called in a certain context. I can summarize my workspace this way:
A "user_code" crate:
This contains a single "user" function, which I would have no control over. Notice that it returns a u32.
pub fn addition(a: u32, b: u32) -> u32 {
a + b
}
A "wrapper" crate:
This contains a macro_rules. This macro creates some modules and a caller function, which will call the user code seen above. This is a highly simplified version. Notice that the return value of addition is ignored.
#[macro_export]
macro_rules! macro_wrapper {
() => {
pub mod my_user_code {
use wrapper::*;
use user_code::*;
pub mod addition {
use wrapper::*;
use super::*;
pub fn caller(a: u32, b: u32) {
addition(a, b);
}
}
}
};
}
An "example" crate:
Here I simply call the wrapper macro, in order to form the modules and functions I need and then use in the main function.
#![deny(unused_results)]
use wrapper::macro_wrapper;
macro_wrapper! {
}
fn main() {
my_user_code::addition::caller(2,3);
println!("Hello, world!");
}
This might seem like super strange code (it is) but it's a very simplified example of a legit project :)
My goal now is to make sure that this code doesn't compile because the return value of addition is not used - it was forgotten. The lint #![deny(unused_results)] seems to achieve exactly what I want.
However, this works only if, instead of use wrapper::macro_wrapper; I have the macro directly defined in the main.rs. In that case I get:
error: unused result of type `u32`
--> example\src\main.rs:14:21
|
14 | addition(a, b);
| ^^^^^^^^^^^^^^^
...
21 | / macro_wrapper! {
22 | |
23 | | }
| |_- in this macro invocation
|
note: the lint level is defined here
--> example\src\main.rs:1:9
|
1 | #![deny(unused_results)]
| ^^^^^^^^^^^^^^
= note: this error originates in the macro `macro_wrapper` (in Nightly builds, run with -Z macro-backtrace for more info)
If the macro is defined in its crate "wrapper", the code compiles and runs just fine without any error. This is not the result I expect.
What am I missing here? Thank you in advance for reading!
The unused_results lint doesn't get fired for macro expansions for external macros. This is by design, because this lint can be very noisy, and you can't control the expansion of external macros.
If you need that, your best bet is to mark your function as #[must_use], then #![deny(unused_must_use)]. Because the unused_must_use lint does get fired for external macros, it will work.
If you can't control the function definition, a neat trick you can use is to define a dummy function whom only job is to trigger the lint:
#[doc(hidden)]
#[must_use]
pub fn must_use<T>(v: T) -> T { v }
// In the macro...
pub fn caller(a: u32, b: u32) {
$crate::must_use(addition(a, b));
}
This question already has an answer here:
What is a crate attribute and where do I add it?
(1 answer)
Closed 4 years ago.
For a no_std application there are a few language items defined in lang_items.rs, one of them being the panic_fmt language item (to specify the behavior of panic! in this no_std context) defined like:
#[lang = "panic_fmt"] #[no_mangle] pub extern fn panic_fmt() -> ! { loop{} }
When compiling, I receive this error:
error[E0522]: definition of an unknown language item: `panic_fmt`
--> src/lang_items.rs:3:1
|
3 | #[lang = "panic_fmt"] #[no_mangle] pub extern fn panic_fmt() -> ! { loop{} }
| ^^^^^^^^^^^^^^^^^^^^^ definition of unknown language item `panic_fmt`
error: `#[panic_implementation]` function required, but not found
After reading RFC 2070 I learned there was a recent breaking change for no_std/embedded programs. While it's recommended that I use the #[panic_implementation] attributes, a recently added feature, I still receive an error, doing so like:
#[panic_implementation] #[no_mangle] pub extern fn panic_fmt() -> ! { loop{} }
Gives the error:
error[E0658]: #[panic_implementation] is an unstable feature (see issue #44489)
--> src/lang_items.rs:4:1
|
4 | #[panic_implementation] #[no_mangle] pub extern fn panic_fmt() -> ! { loop{} }
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add #![feature(panic_implementation)] to the crate attributes to enable
Following their suggestion of adding #![feature(panic_implementation)] to the top of the lang_items.rs file doesn't seem to do the trick, as I'm getting the same error. How do I enable this unstable feature properly so that I can compile this no_std application with behavior for panic! defined?
Okay, it was a simple mistake I was making. Here is a link to the playground containing my code. First of all, I needed to add the crate attribute to the top of my crate root (which, for me, was lib.rs). lang_items.rs was just a file that was used in lib.rs like so:
#![feature(compiler_builtins_lib, lang_items, asm, panic_implementation, core_intrinsics)]
#![no_builtins]
#![no_std]
pub mod lang_items;
const GPIO_BASE: usize = 0x3F000000 + 0x200000;
const GPIO_FSEL1: *mut u32 = (GPIO_BASE + 0x04) as *mut u32;
const GPIO_SET0: *mut u32 = (GPIO_BASE + 0x1C) as *mut u32;
const GPIO_CLR0: *mut u32 = (GPIO_BASE + 0x28) as *mut u32;
#[inline(never)]
fn spin_sleep_ms(ms: usize) {
for _ in 0..(ms * 600) {
unsafe { asm!("nop" :::: "volatile"); }
}
}
#[no_mangle]
pub unsafe extern "C" fn kmain() {
// STEP 1: Set GPIO Pin 16 as output.
GPIO_FSEL1.write_volatile(0x1 << 0x12);
// STEP 2: Continuously set and clear GPIO 16.
loop {
GPIO_SET0.write_volatile(0x1 << 0x10);
spin_sleep_ms(256);
GPIO_CLR0.write_volatile(0x1 << 0x10);
spin_sleep_ms(256);
}
}
mexPrintf, just like printf, accepts a varargs list of arguments, but I don't know what the best way to wrap this is in Rust. There is a RFC for variadic generics, but what can we do today?
In this example, I want to print of the number of inputs and outputs, but the wrapped function just prints garbage. Any idea how to fix this?
#![allow(non_snake_case)]
#![allow(unused_variables)]
extern crate mex_sys;
use mex_sys::mxArray;
use std::ffi::CString;
use std::os::raw::c_int;
use std::os::raw::c_void;
type VarArgs = *mut c_void;
// attempt to wrap mex_sys::mexPrintf
fn mexPrintf(fmt: &str, args: VarArgs) {
let cs = CString::new(fmt).unwrap();
unsafe {
mex_sys::mexPrintf(cs.as_ptr(), args);
}
}
#[no_mangle]
pub extern "system" fn mexFunction(
nlhs: c_int,
plhs: *mut *mut mxArray,
nrhs: c_int,
prhs: *mut *mut mxArray,
) {
let hw = CString::new("hello world\n").unwrap();
unsafe {
mex_sys::mexPrintf(hw.as_ptr());
}
let inout = CString::new("%d inputs and %d outputs\n").unwrap();
unsafe {
mex_sys::mexPrintf(inout.as_ptr(), nrhs, nlhs);
}
mexPrintf("hello world wrapped\n", std::ptr::null_mut());
let n = Box::new(nrhs);
let p = Box::into_raw(n);
mexPrintf("inputs %d\n", p as VarArgs);
let mut v = vec![3];
mexPrintf("vec %d\n", v.as_mut_ptr() as VarArgs);
}
Contrary to popular belief, it is possible to call variadic / vararg functions that were defined in C. That doesn't mean that doing so is very easy, and it's definitely even easier to do something bad because there are even fewer types for the compiler to check your work with.
Here's an example of calling printf. I've hard-coded just about everything:
extern crate libc;
fn my_thing() {
unsafe {
libc::printf(b"Hello, %s (%d)\0".as_ptr() as *const i8, b"world\0".as_ptr(), 42i32);
}
}
fn main() {
my_thing()
}
Note that I have to very explicitly make sure my format string and arguments are all the right types and the strings are NUL-terminated.
Normally, you'll use tools like CString:
extern crate libc;
use std::ffi::CString;
fn my_thing(name: &str, number: i32) {
let fmt = CString::new("Hello, %s (%d)").expect("Invalid format string");
let name = CString::new(name).expect("Invalid name");
unsafe {
libc::printf(fmt.as_ptr(), name.as_ptr(), number);
}
}
fn main() {
my_thing("world", 42)
}
The Rust compiler test suite also has an example of calling a variadic function.
A word of warning specifically for printf-like functions: C compiler-writers realized that people screw up this particular type of variadic function call all the time. To help combat that, they've encoded special logic that parses the format string and attempts to check the argument types against the types the format string expect. The Rust compiler will not check your C-style format strings for you!
I had confused a "variable list of arguments" with a va_list. I'm going to avoid both if I can and in this situation, I'm just going to do the string formatting in Rust before passing it to interop. Here is what worked for me in this case:
#![allow(non_snake_case)]
#![allow(unused_variables)]
extern crate mex_sys;
use mex_sys::mxArray;
use std::ffi::CString;
use std::os::raw::c_int;
// attempt to wrap mex_sys::mexPrintf
fn mexPrintf(text: &str) {
let cs = CString::new(text).expect("Invalid text");
unsafe {
mex_sys::mexPrintf(cs.as_ptr());
}
}
#[no_mangle]
pub extern "C" fn mexFunction(
nlhs: c_int,
plhs: *mut *mut mxArray,
nrhs: c_int,
prhs: *mut *mut mxArray,
) {
mexPrintf(&format!("{} inputs and {} outputs\n", nrhs, nlhs));
}