-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Newtype doesn't transparently wrap integers when calling FFI from Emscripten #41483
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
Comments
FWIW |
It is intentional that newtypes act as structs and are not transparent, because they are structs, just with only one field. If you want transparency you need |
@retep998 Thanks for the pointer to that RFC. However, it doesn't seem right that behaviour is inconsistent across platforms even with
The intentional bit sounds strange given how often newtype pattern is recommended in Rust as "transparent" way to apply custom traits onto some type without affecting its real representation and behaviour. And especially given that it behaves inconsistently in return position and argument position, making roundtrip impossible without manual conversions despite same type being used in both. |
Does the equivalent C code produce the same issue on emscripten? That's the reference point for |
@rkruppe Yeah, it does, which is why I said above
It's just that, unlike C, it's Rust that often recommends newtypes everywhere where you want to add custom behaviour transparently, so one might expect it to care about lowering on its side (or maybe it's just me and I'm wrong). I can report issue to Emscripten instead, but figured should start here to see if this is considered as either 1) a bug or 2) an optimization opportunity in Rust itself. |
If C code does it too, then the C ABI says single-integer structures are different from plain integers. There's not much we can do about that on the Rust side,
Finally, two points that make it less urgent to do anything about this:
|
Ok, thanks for the detailed explanation, your points make sense, although it's indeed
As for
^ that's exactly what I'm doing and providing safe interface outside, but due to the amount of functions that have to deal with various "classes" of same-sized integers, wanted a way to ensure internal consistency as well so that I don't accidentally pass integer retrieved from the function that deals with one type of ids to another one. Hence this attempt with newtypes which works everywhere else but not when targetting Emscripten :( As a workaround, for now I can use separate dummy structs and declare these as pointers to these structs, so that they're still type-checked by default, but that's also 1) quite inconvenient and 2) still too easy to break with casts. All hopes for the |
There is definitely a difference between a C struct with a single field and the single field itself. Many calling conventions will distinguish between aggregates and primitives, even if they are the same size and alignment. |
Ah yeah unfortunately as I think others have noted this isn't a bug that we can fix in rustc (yet). It sounds like struct Foo(u32);
extern {
fn as_struct(a: Foo);
fn as_u32(a: u32);
} On some platforms (like linux x86) I believe these have the exact same assembly generated when calling, but as you've noticed other platforms (like emscripten) have different code generated. I think for something like arm and/or aarch64 they're also different, but I forget the precise details. I don't think this is a bug for rustc, though, so closing. Let me know if I'm wrong though! |
Yeah, thanks again everyone for elaboration. |
The newtype pattern is useful on FFI structs to have strongly-typed variations of a base integer type when calling FFI functions:
This works well with native targets, where such calls get lowered to just passing inner value on the stack:
However, when targeting Emscripten (e.g. asmjs), it changes behaviour by stack-allocating the structure and passing a pointer to it:
So, for example, linking with a static library generated from the following C code:
results in printing
100
on every other target, but prints the integer value of a stack pointer (e.g.15812
) on Emscripten targets.Same happens when linking with Emscripten JS libraries (using
--js-library
or upcoming #41409).Of course, as a workaround, I can dereference pointer on callee side every time, but this is both inefficient, and also seems that this inconsistent behaviour is rather a bug that needs to be fixed.
I can see that newtypes actually map to LLVM structures and not integers:
so this might be considered as a bug on Emscripten side which doesn't lower such structures, or lowering could happen on Rust side to ensure consistency - not sure which side is appropriate.
cc @alexcrichton @kripken
The text was updated successfully, but these errors were encountered: