Skip to content

Nullable-pointer Options aren't FFI-compatible with the base pointer types. #11303

Closed
@jld

Description

@jld
#[inline(never)]
extern "C" fn foo(x: ~int) -> Option<~int> { Some(x) }

pub fn main() {
  unsafe {
    let f: extern "C" fn(~int) -> ~int = ::std::cast::transmute(foo);
    assert_eq!(*f(~0xDEADBEE), 0xDEADBEE);
  }
}

Works on x86_64; crashes on i686. The Rust struct or enum gets an LLVM struct wrapped around it, which changes the ABI on 32-bit x86 if the type is used as a return type. This is an unpleasant surprise for projects using the FFI to represent nullable pointers from C, and there isn't an immediately obvious workaround.

But also:

struct AlmostInt(int);

#[inline(never)]
extern "C" fn foo(x: int) -> AlmostInt { AlmostInt(x) }

pub fn main() {
  unsafe {
    let f: extern "C" fn(int) -> int = ::std::cast::transmute(foo);
    assert_eq!(f(0xDEADBEE), 0xDEADBEE);
  }
}

Same thing. But we probably don't want one-field structs to deviate from the ABI for the equivalent C struct, and the newtype case can (I think?) just use a wrapper to do the conversion instead. This yields the slightly counterintuitive result that adjoining a value to a type makes its representation slightly more efficient (option returned in register vs. newtype returned on stack), but we might be willing to live with that.

See also #10570; cc @bjz @rlane

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-FFIArea: Foreign function interface (FFI)A-codegenArea: Code generationO-x86_32Target: x86 processors, 32 bit (like i686-*) (IA-32)P-mediumMedium priority

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions