-
Notifications
You must be signed in to change notification settings - Fork 392
native-lib: allow passing structs as arguments #4466
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
ca18991
to
4e6a464
Compare
|
||
fn main() { | ||
let pass_me = PassMe { value: 42, other_value: 1337 }; | ||
unsafe { pass_struct(pass_me) }; //~ ERROR: Undefined Behavior: passing a non-#[repr(C)] struct over FFI |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this correct? I think it's UB, but maybe if the user intentionally sets #[allow(improper_ctypes)]
we might just want to report this as unsupported instead of UB?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, reporting this as "unsupported" is better.
1faf540
to
e9670f0
Compare
Think this is ready for review. I split off a bunch of the new code this introduces into a different file to not pollute @rustbot ready |
7cd63db
to
532a735
Compare
Seems like there's some trouble here, nvm. @rustbot author |
Reminder, once the PR becomes ready for a review, use |
809f60e
to
edfa595
Compare
Should be fixed. Also bumped the libffi version since apparently there was a soundness issue that got resolved since the previous version we were on; that wasn't the cause of my test failures, but it's probably smart to include it anyways. There's a couple @rustbot ready |
685a17c
to
a5c0d97
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! libffi is much cooler than I expected. :)
I have some ideas for how we could unify the scalar and struct handling... but I think that's better done in a follow-up PR, so I'll post some things on Zulip.
@rustbot author
unsafe { cif.call(fun, &arg_ptrs) } | ||
} | ||
|
||
/// A wrapper type for `libffi::middle::Type` which also holds a pointer to the data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain what this means in a self-contained way, rather than referencing other concepts the reader is unlikely to know?
Also, it's odd to lead with this being a "type" when it is called "arg".
} | ||
} | ||
|
||
/// An owning form of `FfiArg`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The prefixes "C" and "Ffi" don't do a great job reflecting "owned" and borrowed.
So what about OwnedArg
and BorrowedArg
? The "downcast" function (also an odd name, what does it downcast?) can then be called borrow
.
CPrimitive
could be called ScalarArg
as that seems to match what we use it for.
Also, please generally define types bottom-up and with increasing complexity/subtlety, to simplify reading the file in order:
- ScalarArg
- OwnedArg
- BorrowedArg
/// Struct with its computed type layout and bytes. | ||
Struct(FfiType, Box<[u8]>), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't have to be a struct, right? It can be any type that FfiType
can represent, which includes basic integers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change the name, presumably the next PR idea which you brought up on Zulip would make this enum redundant anyway
|
||
fn main() { | ||
let pass_me = PassMe { value: 42, other_value: 1337 }; | ||
unsafe { pass_struct(pass_me) }; //~ ERROR: Undefined Behavior: passing a non-#[repr(C)] struct over FFI |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, reporting this as "unsupported" is better.
/// Test passing a basic struct as an argument. | ||
fn test_pass_struct() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file these tests are in is called "scalar_arguments". Do these arguments you are passing here ,ook like scalars to you? :)
Please make a new file for aggregate arguments.
args: &'tcx ty::List<ty::GenericArg<'tcx>>, | ||
) -> InterpResult<'tcx, FfiType> { | ||
// TODO: is this correct? Maybe `repr(transparent)` when the inner field | ||
// is itself `repr(c)` is ok? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that would be okay. But let's leave it to a future PR, since a repr(transparent)
wrapper around e.g. i32
should also be supported and that'll be a bit more interesting -- it's going to be an Immediate
.
if let Some(prov) = ptr.provenance { | ||
// The first time this happens, print a warning. | ||
if !this.machine.native_call_mem_warned.replace(true) { | ||
// Newly set, so first time we get here. | ||
this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem { tracing }); | ||
} | ||
|
||
this.expose_provenance(prov)?; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same snippet will be needed in the struct case, so please make this a helper function.
part_2: Part2 { bits: 0xabcdef01 }, | ||
part_3: 0xabcdef01, | ||
}; | ||
assert_eq!(unsafe { pass_struct_complex(complex) }, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a fail test that looks roughly like this, to ensure we do check initialization properly?
let arg = MaybeUninit::<ComplexStruct>::uninit();
pass_struct_complex(*arg.as_ptr());
This needs derive(Copy, Clone)
on the struct. (Don't use .read()
, that might defeat the test.)
Co-authored-by: Ralf Jung <[email protected]>
Blocked on #4456 currently to make rebasing easier when that lands.This allows passing arbitrary structs as arguments to native code; receiving a struct as a return value is not yet implemented. Also includes a couple of tests.I figured the review queue looked a bit too empty :D