rust-lang/rust#157973 will hopefully soon land, fixing our current ABI compatibility rules. As part of that we are introducing a new concept, that of types with "trivial ABI".
This provides a nice opportunity to go further: we could declare that arguments with "trivial ABI" can be entirely filtered out of the signature before comparing the caller and callee view of the call. So for instance, the caller could use signature fn(i32, ()) and the callee could use fn(MyZst, i32), and we'd still consider this to be ABI-compatible.
I think this has several benefits:
- It is already how the compiler works: arguments with
PassMode::Ignore at not even present in the generated LLVM IR, and "trivial ABI" is pretty much the surface level term for "types that are guaranteed to use PassMode::Ignore". We are therefore not making any assumptions about what LLVM does here, or about any target-specific ABIs; we are only making promises about things that are fully under the contol of rustc. "Trivial ABI" is also carefully designed to only apply to types that are "owned" by Rust, i.e., we are fully in control of what we want their ABI to be.
- The compiler in fact already internally relies on this when turning a captureless closure into a function pointer: the underlying function has type
fn(self, args...) -> ret but the function pointer has type fn(args...) -> ret. This works because self of a captureless closure has trivial ABI. But because we don't have a general rule for this, we currently need awkward special reasoning in the compiler that relies on this, and awkward special treatment in Miri to allow this special case without allowing the general case.
- It is something people already often assume to be true, and it's good to align reality with people's expectations if we can cheaply do so.
Given that these arguments are already not present in LLVM, I think this is also fine from a CFI perspective (Cc @Darksonn @rcvalle) -- the sanitizer already today doesn't even know that these arguments exist.
rust-lang/rust#157973 will hopefully soon land, fixing our current ABI compatibility rules. As part of that we are introducing a new concept, that of types with "trivial ABI".
This provides a nice opportunity to go further: we could declare that arguments with "trivial ABI" can be entirely filtered out of the signature before comparing the caller and callee view of the call. So for instance, the caller could use signature
fn(i32, ())and the callee could usefn(MyZst, i32), and we'd still consider this to be ABI-compatible.I think this has several benefits:
PassMode::Ignoreat not even present in the generated LLVM IR, and "trivial ABI" is pretty much the surface level term for "types that are guaranteed to usePassMode::Ignore". We are therefore not making any assumptions about what LLVM does here, or about any target-specific ABIs; we are only making promises about things that are fully under the contol of rustc. "Trivial ABI" is also carefully designed to only apply to types that are "owned" by Rust, i.e., we are fully in control of what we want their ABI to be.fn(self, args...) -> retbut the function pointer has typefn(args...) -> ret. This works becauseselfof a captureless closure has trivial ABI. But because we don't have a general rule for this, we currently need awkward special reasoning in the compiler that relies on this, and awkward special treatment in Miri to allow this special case without allowing the general case.Given that these arguments are already not present in LLVM, I think this is also fine from a CFI perspective (Cc @Darksonn @rcvalle) -- the sanitizer already today doesn't even know that these arguments exist.