diff --git a/common/crubit_abi_type.rs b/common/crubit_abi_type.rs index 78b324c5e..e2f02f2f4 100644 --- a/common/crubit_abi_type.rs +++ b/common/crubit_abi_type.rs @@ -68,6 +68,13 @@ pub enum CrubitAbiType { rust_type: FullyQualifiedPath, cpp_type: TokenStream, }, + /// A type that is not trivially relocatable, so it cannot be passed by value in the ABI. + /// It is represented as `impl ::ctor::Ctor` in + /// Rust. + Immovable { + rust_type: FullyQualifiedPath, + cpp_type: TokenStream, + }, /// A proto message type. This is a special case of CrubitAbiType::Type, where the Rust type is /// a ::foo_proto::ProtoMessageRustBridge, and the C++ type is a /// ::crubit::BoxedAbi<::foo_proto::Message>. @@ -193,6 +200,10 @@ impl ToTokens for CrubitAbiTypeToRustTokens<'_> { CrubitAbiType::Transmute { rust_type, .. } => { quote! { ::bridge_rust::TransmuteAbi<#rust_type> }.to_tokens(tokens); } + CrubitAbiType::Immovable { rust_type, .. } => { + quote! { impl ::ctor::Ctor } + .to_tokens(tokens); + } CrubitAbiType::ProtoMessage { proto_message_rust_bridge, rust_proto_path, .. } => { quote! { #proto_message_rust_bridge<#rust_proto_path> }.to_tokens(tokens); } @@ -264,6 +275,9 @@ impl ToTokens for CrubitAbiTypeToRustExprTokens<'_> { CrubitAbiType::Transmute { rust_type, .. } => { quote! { ::bridge_rust::transmute_abi::<#rust_type>() }.to_tokens(tokens); } + CrubitAbiType::Immovable { .. } => { + quote! { ::core::marker::PhantomData }.to_tokens(tokens); + } CrubitAbiType::ProtoMessage { proto_message_rust_bridge, .. } => { quote! { #proto_message_rust_bridge(::core::marker::PhantomData) } .to_tokens(tokens); @@ -333,6 +347,9 @@ impl ToTokens for CrubitAbiTypeToCppTokens<'_> { CrubitAbiType::Transmute { cpp_type, .. } => { quote! { ::crubit::TransmuteAbi<#cpp_type> }.to_tokens(tokens); } + CrubitAbiType::Immovable { cpp_type, .. } => { + quote! { ::crubit::BoxedAbi<#cpp_type> }.to_tokens(tokens); + } CrubitAbiType::ProtoMessage { cpp_proto_path, .. } => { quote! { ::crubit::BoxedAbi<#cpp_proto_path> }.to_tokens(tokens); } @@ -405,6 +422,9 @@ impl ToTokens for CrubitAbiTypeToCppExprTokens<'_> { CrubitAbiType::Transmute { cpp_type, .. } => { quote! { ::crubit::TransmuteAbi<#cpp_type>() }.to_tokens(tokens); } + CrubitAbiType::Immovable { cpp_type, .. } => { + quote! { ::crubit::BoxedAbi<#cpp_type>() }.to_tokens(tokens); + } CrubitAbiType::ProtoMessage { cpp_proto_path, .. } => { quote! { ::crubit::BoxedAbi<#cpp_proto_path>() }.to_tokens(tokens); } @@ -486,4 +506,29 @@ mod tests { .to_string() ); } + + #[gtest] + fn immovable_test() { + let rust_type = FullyQualifiedPath::new("crate::Nontrivial"); + let cpp_type = quote! { Nontrivial }; + + let abi = + CrubitAbiType::Immovable { rust_type: rust_type.clone(), cpp_type: cpp_type.clone() }; + + let rust_tokens = CrubitAbiTypeToRustTokens(&abi).to_token_stream().to_string(); + expect_eq!( + rust_tokens, + quote! { impl ::ctor::Ctor } + .to_string() + ); + + let cpp_tokens = CrubitAbiTypeToCppTokens(&abi).to_token_stream().to_string(); + expect_eq!(cpp_tokens, quote! { ::crubit::BoxedAbi<#cpp_type> }.to_string()); + + let rust_expr_tokens = CrubitAbiTypeToRustExprTokens(&abi).to_token_stream().to_string(); + expect_eq!(rust_expr_tokens, quote! { ::core::marker::PhantomData }.to_string()); + + let cpp_expr_tokens = CrubitAbiTypeToCppExprTokens(&abi).to_token_stream().to_string(); + expect_eq!(cpp_expr_tokens, quote! { ::crubit::BoxedAbi<#cpp_type>() }.to_string()); + } } diff --git a/rs_bindings_from_cc/generate_bindings/generate_bindings_test.rs b/rs_bindings_from_cc/generate_bindings/generate_bindings_test.rs index 0bed032bb..49d93613c 100644 --- a/rs_bindings_from_cc/generate_bindings/generate_bindings_test.rs +++ b/rs_bindings_from_cc/generate_bindings/generate_bindings_test.rs @@ -1372,3 +1372,30 @@ fn test_existing_rust_type_assert_incomplete() -> Result<()> { ); Ok(()) } + +#[gtest] +fn test_nontrivial_in_bridge_type_generates_ok() -> Result<()> { + let ir = ir_from_cc( + r#" + namespace ns { + template + struct pair { + T1 first; + T2 second; + }; + } + struct Nontrivial { + ~Nontrivial() {} + }; + ns::pair Func(); + "#, + )?; + let bindings = generate_bindings_tokens_for_test(ir)?; + assert_rs_matches!( + bindings.rs_api, + quote! { + pub fn Func() -> ::ctor::Ctor![crate::__CcTemplateInstN2ns4pairI10NontrivialiEE] {...} + } + ); + Ok(()) +} diff --git a/rs_bindings_from_cc/generate_bindings/lib.rs b/rs_bindings_from_cc/generate_bindings/lib.rs index 8f5871a92..90fc7a5ca 100644 --- a/rs_bindings_from_cc/generate_bindings/lib.rs +++ b/rs_bindings_from_cc/generate_bindings/lib.rs @@ -848,12 +848,6 @@ fn crubit_abi_type(db: &dyn BindingsGenerator, rs_type_kind: RsTypeKind) -> Resu } }, RsTypeKind::Record { record, crate_path, .. } => { - ensure!( - record.is_unpin(), - "Type `{}` must be Rust-movable in order to memcpy through a bridge buffer. See crubit.rs/cpp/classes_and_structs#rust_movable", - record.cc_name - ); - let rust_type = crate_path .to_fully_qualified_path(make_rs_ident(record.rs_name.identifier.as_ref())); @@ -879,7 +873,11 @@ fn crubit_abi_type(db: &dyn BindingsGenerator, rs_type_kind: RsTypeKind) -> Resu let cpp_type = make_cpp_type_from_item(record.as_ref(), cc_name_parts, db)?.to_token_stream(); - Ok(CrubitAbiType::Transmute { rust_type, cpp_type }) + if record.is_unpin() { + Ok(CrubitAbiType::Transmute { rust_type, cpp_type }) + } else { + Ok(CrubitAbiType::Immovable { rust_type, cpp_type }) + } } _ => bail!("Unsupported RsTypeKind: {}", rs_type_kind.display(db)), }