Skip to content

Newtype doesn't transparently wrap integers when calling FFI from Emscripten #41483

Closed
@RReverser

Description

@RReverser

The newtype pattern is useful on FFI structs to have strongly-typed variations of a base integer type when calling FFI functions:

#[repr(C)]
struct MySpecialId(u32);

extern {
  fn get_special_id() -> MySpecialId;
  fn do_smth_with_special_id(id: MySpecialId);
}

pub fn main() {
  unsafe {
    let id = get_special_id();
    do_smth_with_special_id(id); // passes
    do_smth_with_special_id(100); // typecheck fails
  }
}

This works well with native targets, where such calls get lowered to just passing inner value on the stack:

#[repr(C)]
struct MySpecialId(u32);

extern {
  fn do_smth_with_special_id(id: MySpecialId);
}

pub fn main() {
  unsafe {
    do_smth_with_special_id(MySpecialId(100));
  }
}
example::main:
        push    rbp
        mov     rbp, rsp
        mov     edi, 100
        pop     rbp
        jmp     do_smth_with_special_id@PLT

However, when targeting Emscripten (e.g. asmjs), it changes behaviour by stack-allocating the structure and passing a pointer to it:

function __ZN4temp4main17h3462209f0fd3a45cE() {
 var $_2 = 0, $_2$byval_copy = 0, sp = 0;
 sp = STACKTOP;
 STACKTOP = STACKTOP + 16 | 0;
 $_2$byval_copy = sp + 4 | 0;
 $_2 = sp;
 HEAP32[$_2 >> 2] = 100;
 HEAP32[$_2$byval_copy >> 2] = HEAP32[$_2 >> 2];
 _do_smth_with_special_id($_2$byval_copy | 0);
 STACKTOP = sp;
 return;
}

So, for example, linking with a static library generated from the following C code:

#include <stdio.h>

extern void do_smth_with_special_id(unsigned id) {
    printf("%u\n", id);
}

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:

declare void @do_smth_with_special_id({ i64 })

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions