dtolnay / cxx

Safe interop between Rust and C++
https://cxx.rs
Apache License 2.0
5.86k stars 332 forks source link

has C-linkage specified, but returns user-defined type which is incompatible with C #1216

Open j-baker opened 1 year ago

j-baker commented 1 year ago

Hi! I have project where I'm trying to expose an 'interface' on the Rust side for C++ to implement. This exposes a C++ header and opaque type which looks something like

#pragma once
#include "rust/cxx.h"

namespace my_namespace
{
  struct SomeType;

  class MyInterface {
  public:
    virtual SomeType doSomething(rust::Fn<rust::Vec<uint8_t>(std::vector<uint8_t> const & inputData) dataProcessor) const = 0;

    virtual ~MyInterface(){};
  }
}

Essentially, it takes a function pointer to an operation implemented in Rust. It's exposed in Rust as

unsafe extern "C++" {
  include!(<myheader.h>);
  type MyInterface;
  #[rust_name="do_something"]
  fn doSomething(&self, data_processor: fn(&CxxVector<u8>) -> Vec<u8>;
}

While this code empirically works, when compiling it issues a warning, complaining that:

warning: /myproject-172d70075ad9da85/out/cxxbridge/sources/myproject/src/lib.rs.cc:1417:29: warning: 'my_namespace$cxxbridge1$MyInterface$do_something$data_processor$0' has C-linkage specified, but returns user-defined type '::rust::Vec< ::std::uint8_t>' (aka 'Vec<unsigned char>') which is incompatible with C [-Wreturn-type-c-linkage]
warning: ::rust::Vec<::std::uint8_t> mynamespace$cxxbridge1$MyInterface$do_something$data_processor$0( ::std::vector<::std::uint8_t> const &arg0, void *extern$) noexcept {
warning:                             ^
warning: 1 warning generated.

Is this related to my declaration or is it a limitation of CXX right now? Is anything bad going to happen? Like I assume many of you, I'm much more familiar with Rust than C++.

ghost commented 1 year ago

Commenting so I can follow the ticket's progress as I'm getting the same issue.

schreter commented 1 year ago

At other places, return types are indirected, here not. Probably it would be helpful to convert the bridge to C++ linkage instead of C linkage.

The warning tells you that there may be ABI incompatibilities. However, as long as you compile and link with the same compiler/linker, you should be on the safe side. Nevertheless, it's still sort-of undefined behavior. We do suppress the warning in our project. Here is the comment from our makefile:

    # Using template types in the Rust client lib leads to warnings emitted by `clang` because
    # technically the interface defines functions as `extern "C"` (necessary because of name
    # mangling). Strictly speaking, those template types are not allowed in return types. Disabling
    # the warning is a hack. Note that `gcc` does not know this warning.
hdoordt commented 6 months ago

Is there any non-hacky way to solve this? What would it take to make cxx not emit 'sort-of undefined behavior'?

schreter commented 6 months ago

Is there any non-hacky way to solve this? What would it take to make cxx not emit 'sort-of undefined behavior'?

I suppose there is none, as of now. Theoretically, you could wrap a specific instantiation of Rust generic (i.e., with fixed type argument) in a newtype without generic and pass that, but passing arbitrary Rust types between C/C++ is also not supported as of now (only in the patches in our private fork).

But as I wrote almost a year ago, this should be no big issue in practice and you can safely ignore the warning, if the compiler/linker version on both sides generates the same layout for rust::Vec<T> (RustVec<T> on the Rust side is marked with #[repr(C)], so it should always be the case for POD T).