Merge pr/457 into pr/456
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 26b2722..be15d31 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -15,8 +15,8 @@
include:
- rust: nightly
- rust: beta
- - rust: stable
- - rust: 1.43.0
+ #- rust: stable
+ #- rust: 1.48.0
- name: macOS
rust: nightly
os: macos
@@ -51,7 +51,7 @@
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- - uses: dtolnay/rust-toolchain@stable
+ - uses: dtolnay/rust-toolchain@beta
- uses: actions/setup-java@v1
with:
java-version: 11
diff --git a/README.md b/README.md
index a962b52..0ae6686 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@
cxx-build = "0.5"
```
-*Compiler support: requires rustc 1.43+ and c++11 or newer*<br>
+*Compiler support: requires rustc 1.48+ and c++11 or newer*<br>
*[Release notes](https://github.com/dtolnay/cxx/releases)*
<br>
@@ -90,7 +90,7 @@
fn next_chunk(buf: &mut MultiBuf) -> &[u8];
}
- extern "C++" {
+ unsafe extern "C++" {
// One or more headers with the matching C++ declarations. Our code
// generators don't read it but it gets #include'd and used in static
// assertions to ensure our picture of the FFI boundary is accurate.
diff --git a/WORKSPACE b/WORKSPACE
index 6093eee..3fc2f90 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -25,15 +25,17 @@
load("@io_bazel_rules_rust//rust:repositories.bzl", "rust_repository_set")
rust_repository_set(
- name = "rust_1_47_linux",
+ name = "rust_1_48_beta_linux",
exec_triple = "x86_64-unknown-linux-gnu",
- version = "1.47.0",
+ iso_date = "2020-11-08",
+ version = "beta",
)
rust_repository_set(
- name = "rust_1_47_darwin",
+ name = "rust_1_48_beta_darwin",
exec_triple = "x86_64-apple-darwin",
- version = "1.47.0",
+ iso_date = "2020-11-08",
+ version = "beta",
)
vendor(
diff --git a/demo/src/main.rs b/demo/src/main.rs
index 10f57e5..dca596a 100644
--- a/demo/src/main.rs
+++ b/demo/src/main.rs
@@ -14,7 +14,7 @@
}
// C++ types and signatures exposed to Rust.
- extern "C++" {
+ unsafe extern "C++" {
include!("demo/include/blobstore.h");
type BlobstoreClient;
diff --git a/gen/lib/tests/test.rs b/gen/lib/tests/test.rs
index ebb69a8..d035b52 100644
--- a/gen/lib/tests/test.rs
+++ b/gen/lib/tests/test.rs
@@ -6,7 +6,7 @@
let rs = quote! {
#[cxx::bridge]
mod ffi {
- extern "C++" {
+ unsafe extern "C++" {
fn in_C();
}
extern "Rust" {
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 0ae7168..903585e 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -563,14 +563,10 @@
});
let vars = receiver_var.into_iter().chain(arg_vars);
+ let wrap_super = invoke.map(|invoke| expand_rust_function_shim_super(sig, &local_name, invoke));
+
let mut call = match invoke {
- Some(ident) => match &sig.receiver {
- None => quote!(super::#ident),
- Some(receiver) => {
- let receiver_type = &receiver.ty;
- quote!(#receiver_type::#ident)
- }
- },
+ Some(_) => quote!(#local_name),
None => quote!(::std::mem::transmute::<*const (), #sig>(__extern)),
};
call.extend(quote! { (#(#vars),*) });
@@ -648,11 +644,63 @@
#[export_name = #link_name]
unsafe extern "C" fn #local_name(#(#all_args,)* #outparam #pointer) #ret {
let __fn = concat!(module_path!(), #catch_unwind_label);
+ #wrap_super
#expr
}
}
}
+// A wrapper like `fn f(x: Arg) { super::f(x) }` just to ensure we have the
+// accurate unsafety declaration and no problematic elided lifetimes.
+fn expand_rust_function_shim_super(
+ sig: &Signature,
+ local_name: &Ident,
+ invoke: &Ident,
+) -> TokenStream {
+ let unsafety = sig.unsafety;
+
+ let receiver_var = sig
+ .receiver
+ .as_ref()
+ .map(|receiver| Ident::new("__self", receiver.var.span));
+ let receiver = sig.receiver.iter().map(|receiver| {
+ let receiver_type = receiver.ty();
+ quote!(#receiver_var: #receiver_type)
+ });
+ let args = sig.args.iter().map(|arg| quote!(#arg));
+ let all_args = receiver.chain(args);
+
+ let ret = if let Some((result, _langle, rangle)) = sig.throws_tokens {
+ let ok = match &sig.ret {
+ Some(ret) => quote!(#ret),
+ None => quote!(()),
+ };
+ let impl_trait = quote_spanned!(result.span=> impl);
+ let display = quote_spanned!(rangle.span=> ::std::fmt::Display);
+ quote!(-> ::std::result::Result<#ok, #impl_trait #display>)
+ } else {
+ expand_return_type(&sig.ret)
+ };
+
+ let arg_vars = sig.args.iter().map(|arg| &arg.ident);
+ let vars = receiver_var.iter().chain(arg_vars);
+
+ let span = invoke.span();
+ let call = match &sig.receiver {
+ None => quote_spanned!(span=> super::#invoke),
+ Some(receiver) => {
+ let receiver_type = &receiver.ty;
+ quote_spanned!(span=> #receiver_type::#invoke)
+ }
+ };
+
+ quote_spanned! {span=>
+ #unsafety fn #local_name(#(#all_args,)*) #ret {
+ #call(#(#vars,)*)
+ }
+ }
+}
+
fn expand_type_alias(alias: &TypeAlias) -> TokenStream {
let doc = &alias.doc;
let ident = &alias.name.rust;
diff --git a/src/extern_type.rs b/src/extern_type.rs
index 008fdc1..d7634c4 100644
--- a/src/extern_type.rs
+++ b/src/extern_type.rs
@@ -30,7 +30,7 @@
/// # mod file1 {
/// #[cxx::bridge(namespace = "example")]
/// pub mod ffi {
-/// extern "C++" {
+/// unsafe extern "C++" {
/// type Demo;
///
/// fn create_demo() -> UniquePtr<Demo>;
@@ -41,7 +41,7 @@
/// // file2.rs
/// #[cxx::bridge(namespace = "example")]
/// pub mod ffi {
-/// extern "C++" {
+/// unsafe extern "C++" {
/// type Demo = crate::file1::ffi::Demo;
///
/// fn take_ref_demo(demo: &Demo);
@@ -80,7 +80,7 @@
///
/// #[cxx::bridge(namespace = "folly")]
/// pub mod ffi {
-/// extern "C++" {
+/// unsafe extern "C++" {
/// include!("rust_cxx_bindings.h");
///
/// type StringPiece = crate::folly_sys::StringPiece;
diff --git a/src/lib.rs b/src/lib.rs
index c72e362..04266fb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,7 +18,7 @@
//!
//! <br>
//!
-//! *Compiler support: requires rustc 1.43+ and c++11 or newer*<br>
+//! *Compiler support: requires rustc 1.48+ and c++11 or newer*<br>
//! *[Release notes](https://github.com/dtolnay/cxx/releases)*
//!
//! <br>
@@ -86,7 +86,7 @@
//! fn next_chunk(buf: &mut MultiBuf) -> &[u8];
//! }
//!
-//! extern "C++" {
+//! unsafe extern "C++" {
//! // One or more headers with the matching C++ declarations. Our code
//! // generators don't read it but it gets #include'd and used in static
//! // assertions to ensure our picture of the FFI boundary is accurate.
diff --git a/syntax/parse.rs b/syntax/parse.rs
index b557584..9509bd8 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -204,7 +204,7 @@
Lang::Rust => {
if foreign_mod.unsafety.is_some() {
let unsafety = foreign_mod.unsafety;
- let abi = foreign_mod.abi;
+ let abi = &foreign_mod.abi;
let span = quote!(#unsafety #abi);
cx.error(span, "extern \"Rust\" block does not need to be unsafe");
}
@@ -253,6 +253,18 @@
}
}
+ if !trusted
+ && items.iter().any(|api| match api {
+ Api::CxxFunction(efn) => efn.unsafety.is_none(),
+ _ => false,
+ })
+ {
+ cx.error(
+ foreign_mod.abi,
+ "block must be declared `unsafe extern \"C++\"` if it contains any safe-to-call C++ functions",
+ );
+ }
+
let mut types = items.iter().filter_map(|item| match item {
Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.name),
Api::TypeAlias(alias) => Some(&alias.name),
diff --git a/tests/cxx_gen.rs b/tests/cxx_gen.rs
index 340e2d7..a0c9ff4 100644
--- a/tests/cxx_gen.rs
+++ b/tests/cxx_gen.rs
@@ -6,7 +6,7 @@
const BRIDGE0: &str = r#"
#[cxx::bridge]
mod ffi {
- extern "C++" {
+ unsafe extern "C++" {
pub fn do_cpp_thing(foo: &str);
}
}
diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs
index b5ed579..8ca5e57 100644
--- a/tests/ffi/extra.rs
+++ b/tests/ffi/extra.rs
@@ -15,7 +15,7 @@
impl UniquePtr<F> {}
impl UniquePtr<G> {}
- extern "C++" {
+ unsafe extern "C++" {
include!("tests/ffi/tests.h");
type D = crate::other::D;
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index 850a2af..ed6740d 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -1,5 +1,6 @@
#![allow(
clippy::boxed_local,
+ clippy::just_underscores_and_digits,
clippy::ptr_arg,
clippy::trivially_copy_pass_by_ref
)]
@@ -115,7 +116,7 @@
i: i32,
}
- extern "C++" {
+ unsafe extern "C++" {
include!("tests/ffi/tests.h");
type C;
diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs
index cf53cde..aff1b1c 100644
--- a/tests/ffi/module.rs
+++ b/tests/ffi/module.rs
@@ -3,7 +3,7 @@
#[rustfmt::skip]
#[cxx::bridge(namespace = "tests")]
pub mod ffi {
- extern "C++" {
+ unsafe extern "C++" {
include!("tests/ffi/tests.h");
type C = crate::ffi::C;
diff --git a/tests/ui/disallow_lifetime.rs b/tests/ui/disallow_lifetime.rs
index b07697a..a4a7b51 100644
--- a/tests/ui/disallow_lifetime.rs
+++ b/tests/ui/disallow_lifetime.rs
@@ -1,6 +1,6 @@
#[cxx::bridge]
mod ffi {
- extern "C++" {
+ unsafe extern "C++" {
type C;
fn f(&'static self);
}
diff --git a/tests/ui/missing_unsafe.rs b/tests/ui/missing_unsafe.rs
new file mode 100644
index 0000000..d8c0a23
--- /dev/null
+++ b/tests/ui/missing_unsafe.rs
@@ -0,0 +1,10 @@
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ fn f(x: i32);
+ }
+}
+
+unsafe fn f(_x: i32) {}
+
+fn main() {}
diff --git a/tests/ui/missing_unsafe.stderr b/tests/ui/missing_unsafe.stderr
new file mode 100644
index 0000000..df1bce2
--- /dev/null
+++ b/tests/ui/missing_unsafe.stderr
@@ -0,0 +1,7 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+ --> $DIR/missing_unsafe.rs:4:12
+ |
+4 | fn f(x: i32);
+ | ^ call to unsafe function
+ |
+ = note: consult the function's documentation for information on how to avoid undefined behavior
diff --git a/tests/ui/reference_to_reference.rs b/tests/ui/reference_to_reference.rs
index 2318533..91fe160 100644
--- a/tests/ui/reference_to_reference.rs
+++ b/tests/ui/reference_to_reference.rs
@@ -1,6 +1,6 @@
#[cxx::bridge]
mod ffi {
- extern "C++" {
+ unsafe extern "C++" {
type ThingC;
fn repro_c(t: &&ThingC);
}
diff --git a/tests/ui/result_no_display.rs b/tests/ui/result_no_display.rs
new file mode 100644
index 0000000..b535677
--- /dev/null
+++ b/tests/ui/result_no_display.rs
@@ -0,0 +1,14 @@
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ fn f() -> Result<()>;
+ }
+}
+
+pub struct NonError;
+
+fn f() -> Result<(), NonError> {
+ Ok(())
+}
+
+fn main() {}
diff --git a/tests/ui/result_no_display.stderr b/tests/ui/result_no_display.stderr
new file mode 100644
index 0000000..d0502f9
--- /dev/null
+++ b/tests/ui/result_no_display.stderr
@@ -0,0 +1,8 @@
+error[E0277]: `NonError` doesn't implement `std::fmt::Display`
+ --> $DIR/result_no_display.rs:4:19
+ |
+4 | fn f() -> Result<()>;
+ | ^^^^^^^^^^ `NonError` cannot be formatted with the default formatter
+ |
+ = help: the trait `std::fmt::Display` is not implemented for `NonError`
+ = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
diff --git a/tests/ui/unnamed_receiver.rs b/tests/ui/unnamed_receiver.rs
index 13c23ae..5f53a0a 100644
--- a/tests/ui/unnamed_receiver.rs
+++ b/tests/ui/unnamed_receiver.rs
@@ -1,6 +1,6 @@
#[cxx::bridge]
mod ffi {
- extern "C++" {
+ unsafe extern "C++" {
type One;
type Two;
fn f(&mut self);
diff --git a/tests/ui/unrecognized_receiver.rs b/tests/ui/unrecognized_receiver.rs
index 5b09c56..eee8259 100644
--- a/tests/ui/unrecognized_receiver.rs
+++ b/tests/ui/unrecognized_receiver.rs
@@ -1,6 +1,6 @@
#[cxx::bridge]
mod ffi {
- extern "C++" {
+ unsafe extern "C++" {
fn f(self: &Unrecognized);
}
}
diff --git a/tests/ui/unsupported_elided.rs b/tests/ui/unsupported_elided.rs
new file mode 100644
index 0000000..4033319
--- /dev/null
+++ b/tests/ui/unsupported_elided.rs
@@ -0,0 +1,20 @@
+use std::marker::PhantomData;
+
+#[cxx::bridge]
+mod ffi {
+ extern "Rust" {
+ type T;
+
+ fn f(t: &T) -> &str;
+ }
+}
+
+pub struct T<'a> {
+ _lifetime: PhantomData<&'a ()>,
+}
+
+fn f<'a>(_t: &T<'a>) -> &'a str {
+ ""
+}
+
+fn main() {}
diff --git a/tests/ui/unsupported_elided.stderr b/tests/ui/unsupported_elided.stderr
new file mode 100644
index 0000000..5136390
--- /dev/null
+++ b/tests/ui/unsupported_elided.stderr
@@ -0,0 +1,11 @@
+error[E0106]: missing lifetime specifier
+ --> $DIR/unsupported_elided.rs:8:24
+ |
+8 | fn f(t: &T) -> &str;
+ | -- ^ expected named lifetime parameter
+ |
+ = help: this function's return type contains a borrowed value, but the signature does not say which one of `t`'s 2 lifetimes it is borrowed from
+help: consider introducing a named lifetime parameter
+ |
+8 | fn f<'a>(t: &'a T) -> &'a str;
+ | ^^^^ ^^^^^ ^^^