Idea for UnsafeFn and calling convention traits


#1

This is an idea which I previously brought up in rust-lang/rust#29625 (the Fn* traits tracking issue) but completely forgot about until reading @RalfJung’s recent post, Two Kinds of Invariants: Safety and Validity.

The issue is that it’s not currently possible to specify (in a generic way) that a function can only be called from an unsafe context.

This is useful in libraries like libloading and inkwell where you routinely produce function pointers that need to be cast to the correct signature and calling convention (see ExecutionEngine::get_function() and TheDan64/inkwell#36). These functions are innately unsafe to call because there’s no guarantee that they have the correct signature, calling convention, or even still exist (e.g. the JIT could be destroyed or dynamic library unloaded from memory), but the type system doesn’t currently have any mechanism for specifying this constraint.

Would it be useful to propose some sort of UnsafeFn trait for functions which are only callable from unsafe code? Also, is there any way to specify something like calling convention via the type system?


#2

I think this is the unsafe equivalent of things like https://github.com/rust-lang/rfcs/pull/2237.

To avoid needing to duplicate all the traits (UnsafeFn, UnsafeAdd, etc) hopefully we can get (strawman syntax) impl unsafe Fn for Foo and where F: unsafe Fn, or something like that.


#3

what if unsafe was a property of the type parameter?

fn foo<unsafe T: Fn>(...) // uses of T are unsafe

#4

This is what makes me think more and more that we need an impure effect.


#5

How would an effects system interact with things like #[repr] and calling convention? Ideally it’d be nice for libloading and inkwell to constrain the calling convention for any function pointers they hand out.


#6

Here is a way you can emulate UnsafeFn in the meantime,using an Unsafe type.This requires wrapping preexisting unsafe functions though.

https://play.rust-lang.org/?gist=c13019a694fd66108c005c6fa5cf1b27&version=stable&mode=debug&edition=2015


#7

How would that differ from unsafe impl Fn for Foo, which already exists (and is e.g. used to implement traits that are safe to use, but unsafe to implement e.g. Send and Sync)?


#8

unsafe impl Add for Foo would be an error, as Add isn’t an unsafe trait. impl unsafe Add for Foo would make foo1 + foo2 only callable in an unsafe context.


#9

Some further discussion to RFC 2237 here: https://github.com/rust-rfcs/const-eval/issues/1


#10

So then technically something like unsafe impl unsafe Add for Foo { /* ...*/ } would be a valid (if redundant) definition? Or would rustc forbid that combination?


#11

I was picturing that, since Add isn’t an unsafe trait, that particular case would be an error. But yes, something like unsafe impl unsafe FixedSizeArray<i32> for Foo would be valid. (Of course, this is all strawman syntax.)