From 4285e5085d51bfbd73370e5ca35d65375306a9b9 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Mon, 5 Nov 2018 10:36:40 -0800 Subject: [PATCH 1/4] initial pass at function pointers --- .../src/representation/function-pointers.md | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 reference/src/representation/function-pointers.md diff --git a/reference/src/representation/function-pointers.md b/reference/src/representation/function-pointers.md new file mode 100644 index 00000000..9316a533 --- /dev/null +++ b/reference/src/representation/function-pointers.md @@ -0,0 +1,114 @@ +# Representation of Function Pointers + +### Terminology + +In Rust, a function pointer type, is either `fn(Args...) -> Ret`, or +`extern "ABI" fn(Args...) -> Ret`. A function pointer is the address of a +function, and have function pointer type. +The pointer is implicit in the `fn` type, +and `fn` types are implicitly `'static`. + +In C, a function pointer type is `Ret (*)(Args...)`, or `Ret ABI (*)(Args...)`, +and values of function pointer type are either a null pointer value, +or the address of a function. + +### Representation + +The ABI and layout of `(extern "ABI")? fn(Args...) -> Ret` +is exactly that of the corresonding C type -- +the lack of a null value does not change this. +On common platforms, this means that `*const ()` and `fn(Args...) -> Ret` have +the same ABI and layout. This is, in fact, guaranteed by POSIX and Windows. +This means that for the vast majority of platforms, + +```rust +fn go_through_pointer(x: fn()) -> fn() { + let ptr = x as *const (); + unsafe { std::mem::transmute::<*const (), fn()>(ptr) } +} +``` + +is both perfectly safe, and, in fact, required for some APIs -- notably, +`GetProcAddress` on Windows requires you to convert from `void (*)()` to +`void*`, to get the address of a variable; +and the opposite is true of `dlsym`, which requires you to convert from +`void*` to `void (*)()` in order to get the address of functions. +This conversion is _not_ guaranteed by Rust itself, however; +simply the implementation. If the underlying platform allows this conversion, +so will Rust. + +However, null values are not supported by the Rust function pointer types -- +just like references, the expectation is that you use `Option` to create +nullable pointers. `Option Ret` will have the exact same ABI +as `fn(Args...) -> Ret`, but additionally allows null pointer values. + + +### Use + +Function pointers are mostly useful for talking to C -- in Rust, you would +mostly use `T: Fn()` instead of `fn()`. If talking to a C API, +the same caveats as apply to other FFI code should be followed. +As an example, we shall implement the following C interface in Rust: + +```c +struct Cons { + int data; + struct Cons *next; +}; + +struct Cons *cons(struct Cons *self, int data); + +/* + notes: + - func must be non-null + - thunk may be null, and shall be passed unchanged to func + - self may be null, in which case no iteration is done +*/ + +void iterate(struct Cons const *self, void (*func)(int, void *), void *thunk); +bool for_all(struct Cons const *self, bool (*func)(int, void *), void *thunk); +``` + +```rust +pub struct Cons { + data: c_int, + next: Option>, +} + +#[no_mangle] +pub extern "C" fn cons(node: Option>, data: c_int) -> Box { + Box::new(Cons { data, next: node }) +} + +#[no_mangle] +pub extern "C" fn iterate( + node: Option<&Cons>, + func: extern fn(i32, *mut c_void), // note - non-nullable + thunk: *mut c_void, // note - this is a thunk, so it's just passed raw +) { + let mut it = node; + while let Some(node) = it { + func(node.data, thunk); + it = node.next.as_ref().map(|x| &**x); + } +} + +#[no_mangle] +pub extern "C" fn for_all( + node: Option<&Cons>, + func: extern fn(i32, *mut c_void) -> bool, + thunk: *mut c_void, +) -> bool { + let mut it = node; + while let Some(node) = node { + if !func(node.data, thunk) { + return false; + } + it = node.next.as_ref().map(|x| &**x); + } +} +``` + +### Unresolved Questions + +- dunno From 95669d8ff5cbbd78f5bb25a5df7e87bf5ad8d5dc Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Mon, 5 Nov 2018 12:33:29 -0800 Subject: [PATCH 2/4] minor nits --- reference/src/representation/function-pointers.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/reference/src/representation/function-pointers.md b/reference/src/representation/function-pointers.md index 9316a533..7ab9a9b9 100644 --- a/reference/src/representation/function-pointers.md +++ b/reference/src/representation/function-pointers.md @@ -2,9 +2,11 @@ ### Terminology -In Rust, a function pointer type, is either `fn(Args...) -> Ret`, or -`extern "ABI" fn(Args...) -> Ret`. A function pointer is the address of a -function, and have function pointer type. +In Rust, a function pointer type, is either `fn(Args...) -> Ret`, +`extern "ABI" fn(Args...) -> Ret`, `unsafe fn(Args...) -> Ret`, and +`unsafe extern "ABI" fn(Args...) -> Ret`. +A function pointer is the address of a function, +and has function pointer type. The pointer is implicit in the `fn` type, and `fn` types are implicitly `'static`. @@ -14,7 +16,7 @@ or the address of a function. ### Representation -The ABI and layout of `(extern "ABI")? fn(Args...) -> Ret` +The ABI and layout of `(unsafe)? (extern "ABI")? fn(Args...) -> Ret` is exactly that of the corresonding C type -- the lack of a null value does not change this. On common platforms, this means that `*const ()` and `fn(Args...) -> Ret` have From 7a19f00be761d656737b2393f3d557fcdb58cd34 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Tue, 6 Nov 2018 12:23:14 -0800 Subject: [PATCH 3/4] add a > --- .../src/representation/function-pointers.md | 232 +++++++++--------- 1 file changed, 116 insertions(+), 116 deletions(-) diff --git a/reference/src/representation/function-pointers.md b/reference/src/representation/function-pointers.md index 7ab9a9b9..63c1f3d9 100644 --- a/reference/src/representation/function-pointers.md +++ b/reference/src/representation/function-pointers.md @@ -1,116 +1,116 @@ -# Representation of Function Pointers - -### Terminology - -In Rust, a function pointer type, is either `fn(Args...) -> Ret`, -`extern "ABI" fn(Args...) -> Ret`, `unsafe fn(Args...) -> Ret`, and -`unsafe extern "ABI" fn(Args...) -> Ret`. -A function pointer is the address of a function, -and has function pointer type. -The pointer is implicit in the `fn` type, -and `fn` types are implicitly `'static`. - -In C, a function pointer type is `Ret (*)(Args...)`, or `Ret ABI (*)(Args...)`, -and values of function pointer type are either a null pointer value, -or the address of a function. - -### Representation - -The ABI and layout of `(unsafe)? (extern "ABI")? fn(Args...) -> Ret` -is exactly that of the corresonding C type -- -the lack of a null value does not change this. -On common platforms, this means that `*const ()` and `fn(Args...) -> Ret` have -the same ABI and layout. This is, in fact, guaranteed by POSIX and Windows. -This means that for the vast majority of platforms, - -```rust -fn go_through_pointer(x: fn()) -> fn() { - let ptr = x as *const (); - unsafe { std::mem::transmute::<*const (), fn()>(ptr) } -} -``` - -is both perfectly safe, and, in fact, required for some APIs -- notably, -`GetProcAddress` on Windows requires you to convert from `void (*)()` to -`void*`, to get the address of a variable; -and the opposite is true of `dlsym`, which requires you to convert from -`void*` to `void (*)()` in order to get the address of functions. -This conversion is _not_ guaranteed by Rust itself, however; -simply the implementation. If the underlying platform allows this conversion, -so will Rust. - -However, null values are not supported by the Rust function pointer types -- -just like references, the expectation is that you use `Option` to create -nullable pointers. `Option Ret` will have the exact same ABI -as `fn(Args...) -> Ret`, but additionally allows null pointer values. - - -### Use - -Function pointers are mostly useful for talking to C -- in Rust, you would -mostly use `T: Fn()` instead of `fn()`. If talking to a C API, -the same caveats as apply to other FFI code should be followed. -As an example, we shall implement the following C interface in Rust: - -```c -struct Cons { - int data; - struct Cons *next; -}; - -struct Cons *cons(struct Cons *self, int data); - -/* - notes: - - func must be non-null - - thunk may be null, and shall be passed unchanged to func - - self may be null, in which case no iteration is done -*/ - -void iterate(struct Cons const *self, void (*func)(int, void *), void *thunk); -bool for_all(struct Cons const *self, bool (*func)(int, void *), void *thunk); -``` - -```rust -pub struct Cons { - data: c_int, - next: Option>, -} - -#[no_mangle] -pub extern "C" fn cons(node: Option>, data: c_int) -> Box { - Box::new(Cons { data, next: node }) -} - -#[no_mangle] -pub extern "C" fn iterate( - node: Option<&Cons>, - func: extern fn(i32, *mut c_void), // note - non-nullable - thunk: *mut c_void, // note - this is a thunk, so it's just passed raw -) { - let mut it = node; - while let Some(node) = it { - func(node.data, thunk); - it = node.next.as_ref().map(|x| &**x); - } -} - -#[no_mangle] -pub extern "C" fn for_all( - node: Option<&Cons>, - func: extern fn(i32, *mut c_void) -> bool, - thunk: *mut c_void, -) -> bool { - let mut it = node; - while let Some(node) = node { - if !func(node.data, thunk) { - return false; - } - it = node.next.as_ref().map(|x| &**x); - } -} -``` - -### Unresolved Questions - -- dunno +# Representation of Function Pointers + +### Terminology + +In Rust, a function pointer type, is either `fn(Args...) -> Ret`, +`extern "ABI" fn(Args...) -> Ret`, `unsafe fn(Args...) -> Ret`, and +`unsafe extern "ABI" fn(Args...) -> Ret`. +A function pointer is the address of a function, +and has function pointer type. +The pointer is implicit in the `fn` type, +and `fn` types are implicitly `'static`. + +In C, a function pointer type is `Ret (*)(Args...)`, or `Ret ABI (*)(Args...)`, +and values of function pointer type are either a null pointer value, +or the address of a function. + +### Representation + +The ABI and layout of `(unsafe)? (extern "ABI")? fn(Args...) -> Ret` +is exactly that of the corresonding C type -- +the lack of a null value does not change this. +On common platforms, this means that `*const ()` and `fn(Args...) -> Ret` have +the same ABI and layout. This is, in fact, guaranteed by POSIX and Windows. +This means that for the vast majority of platforms, + +```rust +fn go_through_pointer(x: fn()) -> fn() { + let ptr = x as *const (); + unsafe { std::mem::transmute::<*const (), fn()>(ptr) } +} +``` + +is both perfectly safe, and, in fact, required for some APIs -- notably, +`GetProcAddress` on Windows requires you to convert from `void (*)()` to +`void*`, to get the address of a variable; +and the opposite is true of `dlsym`, which requires you to convert from +`void*` to `void (*)()` in order to get the address of functions. +This conversion is _not_ guaranteed by Rust itself, however; +simply the implementation. If the underlying platform allows this conversion, +so will Rust. + +However, null values are not supported by the Rust function pointer types -- +just like references, the expectation is that you use `Option` to create +nullable pointers. `Option Ret>` will have the exact same ABI +as `fn(Args...) -> Ret`, but additionally allows null pointer values. + + +### Use + +Function pointers are mostly useful for talking to C -- in Rust, you would +mostly use `T: Fn()` instead of `fn()`. If talking to a C API, +the same caveats as apply to other FFI code should be followed. +As an example, we shall implement the following C interface in Rust: + +```c +struct Cons { + int data; + struct Cons *next; +}; + +struct Cons *cons(struct Cons *self, int data); + +/* + notes: + - func must be non-null + - thunk may be null, and shall be passed unchanged to func + - self may be null, in which case no iteration is done +*/ + +void iterate(struct Cons const *self, void (*func)(int, void *), void *thunk); +bool for_all(struct Cons const *self, bool (*func)(int, void *), void *thunk); +``` + +```rust +pub struct Cons { + data: c_int, + next: Option>, +} + +#[no_mangle] +pub extern "C" fn cons(node: Option>, data: c_int) -> Box { + Box::new(Cons { data, next: node }) +} + +#[no_mangle] +pub extern "C" fn iterate( + node: Option<&Cons>, + func: extern fn(i32, *mut c_void), // note - non-nullable + thunk: *mut c_void, // note - this is a thunk, so it's just passed raw +) { + let mut it = node; + while let Some(node) = it { + func(node.data, thunk); + it = node.next.as_ref().map(|x| &**x); + } +} + +#[no_mangle] +pub extern "C" fn for_all( + node: Option<&Cons>, + func: extern fn(i32, *mut c_void) -> bool, + thunk: *mut c_void, +) -> bool { + let mut it = node; + while let Some(node) = node { + if !func(node.data, thunk) { + return false; + } + it = node.next.as_ref().map(|x| &**x); + } +} +``` + +### Unresolved Questions + +- dunno From 213250009a99582a2bc95d818de06888e62fa432 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Thu, 10 Jan 2019 21:12:56 -0800 Subject: [PATCH 4/4] correct lifetime stuff --- reference/src/representation/function-pointers.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/reference/src/representation/function-pointers.md b/reference/src/representation/function-pointers.md index 63c1f3d9..89785261 100644 --- a/reference/src/representation/function-pointers.md +++ b/reference/src/representation/function-pointers.md @@ -8,7 +8,16 @@ In Rust, a function pointer type, is either `fn(Args...) -> Ret`, A function pointer is the address of a function, and has function pointer type. The pointer is implicit in the `fn` type, -and `fn` types are implicitly `'static`. +and they have no lifetime of their own; +therefore, function pointers are assumed to point to +a block of code with static lifetime. +This is not necessarily always true, +since, for example, you can unload a dynamic library. +Therefore, this is _only_ a safety invariant, +not a validity invariant; +as long as one doesn't call a function pointer which points to freed memory, +it is not undefined behavior. + In C, a function pointer type is `Ret (*)(Args...)`, or `Ret ABI (*)(Args...)`, and values of function pointer type are either a null pointer value,