Description
Code
#![forbid(unsafe_op_in_unsafe_fn)]
use core::ptr::addr_of;
use core::ptr::addr_of_mut;
pub struct Foo {
field: u8
}
pub unsafe fn foo(ptr: *const Foo) -> *const u8 {
addr_of!((*ptr).field)
}
pub unsafe fn foo_mut(ptr: *mut Foo) -> *mut u8 {
addr_of_mut!((*ptr).field)
}
Current output
<No error>
Desired output
error[E0133]: dereference of raw pointer is unsafe and requires unsafe block
--> src/lib.rs:11:5
|
11 | addr_of!((*ptr).field)
| ^^^^^^^^^^^^^^^^^^^^^^ dereference of raw pointer
|
= note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
= note: this error originates in the macro `addr_of` (in Nightly builds, run with -Z macro-backtrace for more info)
note: the lint level is defined here
--> src/lib.rs:1:11
|
1 | #![forbid(unsafe_op_in_unsafe_fn)]
| ^^^^^^^^^^^^^^^^^^^^^^
error[E0133]: dereference of raw pointer is unsafe and requires unsafe block
--> src/lib.rs:15:5
|
15 | addr_of_mut!((*ptr).field)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ dereference of raw pointer
|
= note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
= note: this error originates in the macro `addr_of_mut` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0133`.
Rationale and extra context
When removing the unsafe
keywords in the function declarations of the sample code, the compiler emits the errors found in the “desired output” box (except for the note on the unsafe_op_in_unsafe_fn
lint). Hence, I was expecting the operations to be unsafe.1 However, with forbid(unsafe_op_in_unsafe_fn)
and the functions marked unsafe
, no errors are emitted at all.
Other cases
No response
Anything else?
Note that the unsafe_op_in_unsafe_fn
lint is not entirely broken for the expr
of addr_of!(expr)
. The following snippet emits an error for calling an unsafe function (but not for dereferencing the raw pointer):
#![forbid(unsafe_op_in_unsafe_fn)]
pub struct Foo {
field: u8
}
unsafe fn unsafe_id<T>(x: T) -> T { x }
pub unsafe fn foo(ptr: *const Foo) -> *const u8 {
core::ptr::addr_of!((*unsafe_id(ptr)).field)
}
Note furthermore that inlining the addr_of!()
/addr_of_mut!()
macros (using #![feature(raw_ref_op)]
) result in the expected behavior:
#![feature(raw_ref_op)]
#![forbid(unsafe_op_in_unsafe_fn)]
pub struct Foo {
field: u8
}
pub unsafe fn foo(ptr: *const Foo) -> *const u8 {
&raw const (*ptr).field
}
pub unsafe fn foo_mut(ptr: *mut Foo) -> *mut u8 {
&raw mut (*ptr).field
}
Footnotes
-
I’m not a hundred percent sure about this point as we effectively perform pointer arithmetic only. Conceptually, I would like to understand
addr_of!((*ptr).field)
as not actually dereferencingptr
. But the docs explicitly state that “theexpr
inaddr_of!(expr)
is still subject to all the usual rules. In particular,addr_of!(*ptr::null())
is Undefined Behavior because it dereferences a null pointer.” ↩