rust-lang / rust-bindgen

Automatically generates Rust FFI bindings to C (and some C++) libraries.
https://rust-lang.github.io/rust-bindgen/
BSD 3-Clause "New" or "Revised" License
4.4k stars 691 forks source link

Cannot call function that returns custom type when destructor is present. #2864

Open lightbulb128 opened 3 months ago

lightbulb128 commented 3 months ago

For reproduction see here. When static Foo Foo::create_foo(int x) is called, throws signal: 11, SIGSEGV: invalid memory reference. But when the destructor is removed, it could work. Furthermore I confirm that when destructor is default, i.e. explicit ~Foo() = default, it also runs.

Input C/C++ Header

//// wrapper.h
#include "foo/foo.h"

//// foo.h
// #include <iostream>

class Foo {
    int x;
public:
    inline Foo(int x) : x(x) {
        // std::cerr << "Creating Foo at " << this << " with x = " << x << std::endl;
    }
    inline static Foo create_foo(int x) {
        // std::cerr << "Calling static create_foo with x = " << x << std::endl;
        return Foo(x);
    }
    inline ~Foo() {
        // std::cerr << "Destroying Foo at " << this << " with x = " << x << std::endl;
    }
};

Bindgen Invocation

// build.rs
use std::env;
use std::path::PathBuf;

fn main() {

    println!("cargo:rerun-if-changed=src/lib.rs");
    println!("cargo:rerun-if-changed=src/wrapper.hpp");
    println!("cargo:rerun-if-changed=../src");

    let mut cmake_config = cmake::Config::new("../");

    // we need to keep the inline functions for gcc
    cmake_config.define("CMAKE_CXX_FLAGS", "-fkeep-inline-functions");

    let out_dir = std::env::var("OUT_DIR").unwrap();

    println!("cargo:rustc-link-search=native={}", out_dir.clone() + "/lib");
    println!("cargo:rustc-link-lib=static=Foo");

    let bindings = bindgen::Builder::default()
        .clang_arg(format!("-I{}", out_dir.clone() + "/include"))
        .clang_args(&["-x", "c++"])
        .generate_inline_functions(true)
        .header("wrapper.h")
        .opaque_type("std::.*")
        .allowlist_type("Foo")
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        .generate()
        .expect("Unable to generate bindings");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}

// lib.rs
mod ffi {
    #![allow(non_upper_case_globals)]
    #![allow(non_camel_case_types)]
    #![allow(non_snake_case)]

    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_foo() {
        let foo = unsafe { ffi::Foo::create_foo(1234) };
    }
}

or

Actual Results

running 1 test
error: test failed, to rerun pass `--lib`

Caused by:
  process didn't exit successfully: `/data/lxq/test-bindgen/rustbind/target/debug/deps/rustbind-0ab6c7ac1c58af6f test_foo` (signal: 11, SIGSEGV: invalid memory reference)

Expected Results

Test should pass.