There are multiple things to pay attention to, here:
-
Should a macro using
unsafebecomeunsafeitself? I don’t think this is desirable. See::std::await!or::pin-utils::pin_mut!: safe constructions using underlyingunsafe. It is the same as a non-unsafefunction wrappingunsafebehavior in a (hopefully) safe manner, thus not needing to beunsafeitself.On the other hand, if a macro expansion cannot be proven to be always sound, then the macro has to be defined without
unsafein it, thus forcing the caller to explicitly use anunsafescope. Sadly, the “unsafeness” of the macro would not be visible at the “header level”; it could just be documented (and maybe also with a naming convention, likeunsafe_foo!), which means that the error message raised might not be great. -
unsafe hygiene, i.e., an input argument
$macro_arg:exprshould not be allowed to “unsafe-ly evaluate”$macro_arg(e.g. feeding*::std::ptr::null()as$macro_arg:exprshould raise an error since it requires "unsafeevaluation", but feedingunsafe { *::std::ptr::null() }as$macro_arg:exprshould be allowed (unsafebeing the keyword making all the evaluations in its scope be automagically taggedsafe, hence its unsafety)). I’m not being very rigurous but you get the idea.In the meantime, the macro maker needs to be careful to never “evaluate an expression argument” (that is, use a
$macro_arg:expr) inside anunsafeblock, using the following pattern:
unsafe fn get_foo () {}
macro_rules! eq_to_foo {(
$macro_arg:expr
) => (
match $macro_arg { macro_arg => unsafe {
// Look, no metavariables within the unsafe block !
macro_arg == get_foo()
}}
)}
// Note that for this very particular case, we could more simply write:
macro_rules! eq_to_foo {(
$macro_arg:expr
) => (
$macro_arg == unsafe { get_foo() }
)}
-
unsafedetection: there are many talks about, as a library user, being able to detect / warn and even forbidunsafeusages. The problem is that current “solutions” do not detectunsafeusage through macro invocations. For instance:-
::cargo-geiger, a tool to walk through the dependency graph and count the number ofunsafeusages encountered in each crate (imho counting is not the best metric, but that is out of topic here):- Unsafe code inside macros are not detected. Needs macro expansion
- Using
#:- It detects if the macro was defined within the same crate. But a macro from a different crate, even when in the same workspace, will sneak its way past the
forbid(unsafe_code)restriction (See here, look at the difference between 1m48 and the end).
- It detects if the macro was defined within the same crate. But a macro from a different crate, even when in the same workspace, will sneak its way past the
-