Allow &mut calls on Trivial extern types only.
Previously we allowed extern types to be used as un-pinned
reference parameters only if there happened to be some other
use of the type in some way which required it to be Trivial.
With this change,
* Opaque extern types must always be pinned when used as mutable
reference parameters;
* Trivial extern types need never be pinned when used as mutable
reference parameters.
diff --git a/syntax/check.rs b/syntax/check.rs
index 3b7b513..80426f9 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -189,7 +189,7 @@
cx.error(
ty,
format!(
- "mutable reference to C++ type requires a pin -- use Pin<&mut {}>",
+ "mutable reference to C++ type requires a pin -- use Pin<&mut {}> or declare the type Trivial in a cxx::ExternType impl",
requires_pin,
),
);
@@ -332,6 +332,10 @@
TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.name.rust),
TrivialReason::BoxTarget => format!("Box<{}>", ety.name.rust),
TrivialReason::VecElement => format!("a vector element in Vec<{}>", ety.name.rust),
+ TrivialReason::UnpinnedMutableReferenceFunctionArgument(efn) => format!(
+ "a non-pinned mutable reference argument of {}",
+ efn.name.rust
+ ),
};
let msg = format!(
"needs a cxx::ExternType impl in order to be used as {}",
@@ -385,7 +389,7 @@
cx.error(
span,
format!(
- "mutable reference to C++ type requires a pin -- use `self: Pin<&mut {}>`",
+ "mutable reference to opaque C++ type requires a pin -- use `self: Pin<&mut {}>` or declare the type Trivial in a cxx::ExternType impl",
receiver.ty.rust,
),
);
diff --git a/syntax/types.rs b/syntax/types.rs
index 72216b3..ddd3c42 100644
--- a/syntax/types.rs
+++ b/syntax/types.rs
@@ -170,14 +170,13 @@
// the APIs above, in case some function or struct references a type
// which is declared subsequently.
let mut required_trivial = Map::new();
- let mut insist_alias_types_are_trivial = |ty: &'a Type, reason| {
- if let Type::Ident(ident) = ty {
- if cxx.contains(&ident.rust)
- && !structs.contains_key(&ident.rust)
- && !enums.contains_key(&ident.rust)
- {
- required_trivial.entry(&ident.rust).or_insert(reason);
- }
+
+ let mut insist_extern_types_are_trivial = |ident: &'a RustName, reason| {
+ if cxx.contains(&ident.rust)
+ && !structs.contains_key(&ident.rust)
+ && !enums.contains_key(&ident.rust)
+ {
+ required_trivial.entry(&ident.rust).or_insert(reason);
}
};
for api in apis {
@@ -185,17 +184,23 @@
Api::Struct(strct) => {
let reason = TrivialReason::StructField(strct);
for field in &strct.fields {
- insist_alias_types_are_trivial(&field.ty, reason);
+ if let Type::Ident(ident) = &field.ty {
+ insist_extern_types_are_trivial(&ident, reason);
+ }
}
}
Api::CxxFunction(efn) | Api::RustFunction(efn) => {
let reason = TrivialReason::FunctionArgument(efn);
for arg in &efn.args {
- insist_alias_types_are_trivial(&arg.ty, reason);
+ if let Type::Ident(ident) = &arg.ty {
+ insist_extern_types_are_trivial(&ident, reason);
+ }
}
if let Some(ret) = &efn.ret {
let reason = TrivialReason::FunctionReturn(efn);
- insist_alias_types_are_trivial(&ret, reason);
+ if let Type::Ident(ident) = &ret {
+ insist_extern_types_are_trivial(&ident, reason);
+ }
}
}
_ => {}
@@ -205,11 +210,37 @@
match ty {
Type::RustBox(ty) => {
let reason = TrivialReason::BoxTarget;
- insist_alias_types_are_trivial(&ty.inner, reason);
+ if let Type::Ident(ident) = &ty.inner {
+ insist_extern_types_are_trivial(&ident, reason);
+ }
}
Type::RustVec(ty) => {
let reason = TrivialReason::VecElement;
- insist_alias_types_are_trivial(&ty.inner, reason);
+ if let Type::Ident(ident) = &ty.inner {
+ insist_extern_types_are_trivial(&ident, reason);
+ }
+ }
+ _ => {}
+ }
+ }
+ for api in apis {
+ match api {
+ Api::CxxFunction(efn) | Api::RustFunction(efn) => {
+ let reason = TrivialReason::UnpinnedMutableReferenceFunctionArgument(efn);
+ if let Some(receiver) = &efn.receiver {
+ if receiver.mutable && !receiver.pinned {
+ insist_extern_types_are_trivial(&receiver.ty, reason);
+ }
+ }
+ for arg in &efn.args {
+ if let Type::Ref(reff) = &arg.ty {
+ if reff.mutable && !reff.pinned {
+ if let Type::Ident(ident) = &reff.inner {
+ insist_extern_types_are_trivial(&ident, reason);
+ }
+ }
+ }
+ }
}
_ => {}
}
@@ -310,6 +341,7 @@
FunctionReturn(&'a ExternFn),
BoxTarget,
VecElement,
+ UnpinnedMutableReferenceFunctionArgument(&'a ExternFn),
}
fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) {
diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs
index a2c13d3..cd9fc2b 100644
--- a/tests/ffi/module.rs
+++ b/tests/ffi/module.rs
@@ -32,12 +32,16 @@
fn c_take_trivial_ptr(d: UniquePtr<D>);
fn c_take_trivial_ref(d: &D);
+ fn c_take_trivial_ref_method(self: &D);
+ fn c_take_trivial_mut_ref_method(self: &mut D);
fn c_take_trivial(d: D);
fn c_take_trivial_ns_ptr(g: UniquePtr<G>);
fn c_take_trivial_ns_ref(g: &G);
fn c_take_trivial_ns(g: G);
fn c_take_opaque_ptr(e: UniquePtr<E>);
fn c_take_opaque_ref(e: &E);
+ fn c_take_opaque_ref_method(self: &E);
+ fn c_take_opaque_mut_ref_method(self: Pin<&mut E>);
fn c_take_opaque_ns_ptr(e: UniquePtr<F>);
fn c_take_opaque_ns_ref(e: &F);
fn c_return_trivial_ptr() -> UniquePtr<D>;
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 2be1e06..1c8a122 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -529,6 +529,18 @@
}
}
+void D::c_take_trivial_ref_method() const {
+ if (d == 30) {
+ cxx_test_suite_set_correct();
+ }
+}
+
+void D::c_take_trivial_mut_ref_method() {
+ if (d == 30) {
+ cxx_test_suite_set_correct();
+ }
+}
+
void c_take_trivial(D d) {
if (d.d == 30) {
cxx_test_suite_set_correct();
@@ -571,6 +583,18 @@
}
}
+void E::c_take_opaque_ref_method() const {
+ if (e == 40 && e_str == "hello") {
+ cxx_test_suite_set_correct();
+ }
+}
+
+void E::c_take_opaque_mut_ref_method() {
+ if (e == 40 && e_str == "hello") {
+ cxx_test_suite_set_correct();
+ }
+}
+
void c_take_opaque_ns_ref(const ::F::F &f) {
if (f.f == 40 && f.f_str == "hello") {
cxx_test_suite_set_correct();
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index 024ecea..f8d983b 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -59,11 +59,15 @@
struct D {
uint64_t d;
+ void c_take_trivial_ref_method() const;
+ void c_take_trivial_mut_ref_method();
};
struct E {
uint64_t e;
std::string e_str;
+ void c_take_opaque_ref_method() const;
+ void c_take_opaque_mut_ref_method();
};
enum COwnedEnum {
diff --git a/tests/test.rs b/tests/test.rs
index 00a7228..b8edf37 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -263,10 +263,14 @@
#[test]
fn test_extern_trivial() {
- let d = ffi2::c_return_trivial();
+ let mut d = ffi2::c_return_trivial();
check!(ffi2::c_take_trivial_ref(&d));
+ check!(d.c_take_trivial_ref_method());
+ check!(d.c_take_trivial_mut_ref_method());
check!(ffi2::c_take_trivial(d));
- let d = ffi2::c_return_trivial_ptr();
+ let mut d = ffi2::c_return_trivial_ptr();
+ check!(d.c_take_trivial_ref_method());
+ check!(d.c_take_trivial_mut_ref_method());
check!(ffi2::c_take_trivial_ptr(d));
cxx::UniquePtr::new(ffi2::D { d: 42 });
let d = ffi2::ns_c_return_trivial();
@@ -282,8 +286,10 @@
#[test]
fn test_extern_opaque() {
- let e = ffi2::c_return_opaque_ptr();
+ let mut e = ffi2::c_return_opaque_ptr();
check!(ffi2::c_take_opaque_ref(e.as_ref().unwrap()));
+ check!(e.c_take_opaque_ref_method());
+ check!(e.pin_mut().c_take_opaque_mut_ref_method());
check!(ffi2::c_take_opaque_ptr(e));
let f = ffi2::c_return_ns_opaque_ptr();
diff --git a/tests/ui/pin_mut_opaque.stderr b/tests/ui/pin_mut_opaque.stderr
index 32d5cfc..417eead 100644
--- a/tests/ui/pin_mut_opaque.stderr
+++ b/tests/ui/pin_mut_opaque.stderr
@@ -16,6 +16,12 @@
9 | fn v(v: &mut CxxVector<u8>);
| ^^^^^^^^^^^^^^^^^^
+error: needs a cxx::ExternType impl in order to be used as a non-pinned mutable reference argument of f
+ --> $DIR/pin_mut_opaque.rs:4:9
+ |
+4 | type Opaque;
+ | ^^^^^^^^^^^
+
error: mutable reference to C++ type requires a pin -- use `self: Pin<&mut Opaque>`
--> $DIR/pin_mut_opaque.rs:6:14
|