diff --git a/gen/src/write.rs b/gen/src/write.rs index 5d47c631..3cd94d10 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1245,7 +1245,12 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { let instance = to_mangled(&out.namespace, ty); let can_construct_from_value = match ty { - Type::Ident(ident) => types.structs.contains_key(ident), + // Some aliases are to opaque types; some are to trivial types. + // We can't know at code generation time, so we generate both C++ + // and Rust side bindings for a "new" method anyway. But that + // Rust code will explode at runtime if anyone tries to call it on + // an opaque type. + Type::Ident(ident) => types.structs.contains_key(ident) || types.aliases.contains_key(ident), _ => false, }; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 6fa7ea7a..4213b4d7 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -814,13 +814,22 @@ fn expand_unique_ptr( let link_release = format!("{}release", prefix); let link_drop = format!("{}drop", prefix); - let new_method = if types.structs.contains_key(ident) { + let new_method = if types.structs.contains_key(ident) || types.aliases.contains_key(ident) { + let trivial_assertion: Option = if types.aliases.contains_key(ident) { + Some(parse_quote! { + < < #ident as :: cxx :: ExternType > :: Kind as :: cxx :: kind :: Kind > :: assert_trivial(); + }) + } else { + None + }; + Some(quote! { fn __new(mut value: Self) -> *mut ::std::ffi::c_void { extern "C" { #[link_name = #link_new] fn __new(this: *mut *mut ::std::ffi::c_void, value: *mut #ident); } + #trivial_assertion let mut repr = ::std::ptr::null_mut::<::std::ffi::c_void>(); unsafe { __new(&mut repr, &mut value) } repr diff --git a/src/extern_type.rs b/src/extern_type.rs index b9c53860..45414c80 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -165,9 +165,17 @@ pub mod kind { /// indirection. pub enum Trivial {} - pub trait Kind: private::Sealed {} - impl Kind for Opaque {} - impl Kind for Trivial {} + pub trait Kind: private::Sealed { + fn assert_trivial(); + } + impl Kind for Opaque { + fn assert_trivial() { + panic!("Type not trivial"); + } + } + impl Kind for Trivial { + fn assert_trivial() {} + } } mod private { diff --git a/tests/ffi/extra.rs b/tests/ffi/extra.rs index a809ea4e..6209702e 100644 --- a/tests/ffi/extra.rs +++ b/tests/ffi/extra.rs @@ -12,12 +12,14 @@ pub mod ffi2 { impl UniquePtr {} impl UniquePtr {} + impl UniquePtr {} extern "C" { include!("tests/ffi/tests.h"); type D = crate::other::D; type E = crate::other::E; + type F = crate::other::F; fn c_take_trivial_ptr(d: UniquePtr); fn c_take_trivial_ref(d: &D); diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 7cffdedd..f80cda79 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -14,15 +14,23 @@ mod other { use cxx::kind::{Opaque, Trivial}; use cxx::{type_id, CxxString, ExternType}; + // Trivial. #[repr(C)] pub struct D { - d: u64, + pub d: u64, } + // Opaque, and has realistic complexity. #[repr(C)] pub struct E { - e: u64, e_str: CxxString, + e: u64, + } + + // Opaque, but simple enough that bad code can try to create it. + #[repr(C)] + pub struct F { + pub f: u64, } unsafe impl ExternType for D { @@ -34,6 +42,11 @@ mod other { type Id = type_id!("tests::E"); type Kind = Opaque; } + + unsafe impl ExternType for F { + type Id = type_id!("tests::F"); + type Kind = Opaque; + } } #[cxx::bridge(namespace = tests)] diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index b3f547e5..ff1fbd3e 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -37,6 +37,10 @@ struct E { std::string e_str; }; +struct F { + uint64_t f; +}; + enum COwnedEnum { CVal1, CVal2, diff --git a/tests/test.rs b/tests/test.rs index a9cd8e16..3ea02fa4 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -199,6 +199,7 @@ fn test_extern_trivial() { check!(ffi2::c_take_trivial(d)); let d = ffi2::c_return_trivial_ptr(); check!(ffi2::c_take_trivial_ptr(d)); + cxx::UniquePtr::new(ffi2::D { d: 42 }); } #[test] @@ -206,4 +207,7 @@ fn test_extern_opaque() { let e = ffi2::c_return_opaque_ptr(); check!(ffi2::c_take_opaque_ref(e.as_ref().unwrap())); check!(ffi2::c_take_opaque_ptr(e)); + assert!(std::panic::catch_unwind(|| { + cxx::UniquePtr::new(ffi2::F { f: 42 }) + }).is_err()); }