diff --git a/cc_bindings_from_rs/generate_bindings/format_type.rs b/cc_bindings_from_rs/generate_bindings/format_type.rs index ccd2ad3f3..523edf6ae 100644 --- a/cc_bindings_from_rs/generate_bindings/format_type.rs +++ b/cc_bindings_from_rs/generate_bindings/format_type.rs @@ -961,9 +961,9 @@ pub fn crubit_abi_type_from_ty<'tcx>( } return Ok(CrubitAbiTypeWithCcPrereqs { crubit_abi_type: CrubitAbiType::Transmute { - rust_type: FullyQualifiedPath { - start_with_colon2: true, - parts: fully_qualified_name.rs_name_parts().collect::>(), + rust_type: { + let parts = fully_qualified_name.rs_name_parts(); + quote! { #(::#parts)* } }, cpp_type: cpp_type.as_str().parse().expect("Malformed cpp_type annotation"), }, @@ -986,9 +986,9 @@ pub fn crubit_abi_type_from_ty<'tcx>( // Question: do we need to check that it doesn't have any generics? CrubitAbiType::Transmute { - rust_type: FullyQualifiedPath { - start_with_colon2: true, - parts: fully_qualified_name.rs_name_parts().collect::>(), + rust_type: { + let parts = fully_qualified_name.rs_name_parts(); + quote! { #(::#parts)* } }, cpp_type: fully_qualified_name.format_for_cc(db)?, } diff --git a/common/crubit_abi_type.rs b/common/crubit_abi_type.rs index 235f1ce3e..d918141da 100644 --- a/common/crubit_abi_type.rs +++ b/common/crubit_abi_type.rs @@ -65,7 +65,7 @@ pub enum CrubitAbiType { in_cc_std: bool, }, Transmute { - rust_type: FullyQualifiedPath, + rust_type: TokenStream, cpp_type: TokenStream, }, /// A proto message type. This is a special case of CrubitAbiType::Type, where the Rust type is @@ -123,7 +123,9 @@ impl CrubitAbiType { pub fn transmute(rust_type: &str, cpp_type: &str) -> Self { CrubitAbiType::Transmute { - rust_type: FullyQualifiedPath::new(rust_type), + rust_type: rust_type.parse().unwrap_or_else(|e| { + panic!("Failed to parse Rust type `{rust_type}` as a TokenStream: {e}") + }), cpp_type: cpp_type.parse().unwrap_or_else(|e| { panic!("Failed to parse C++ type `{cpp_type}` as a TokenStream: {e}") }), diff --git a/rs_bindings_from_cc/generate_bindings/database/rs_snippet.rs b/rs_bindings_from_cc/generate_bindings/database/rs_snippet.rs index d49db4c76..eb9457c34 100644 --- a/rs_bindings_from_cc/generate_bindings/database/rs_snippet.rs +++ b/rs_bindings_from_cc/generate_bindings/database/rs_snippet.rs @@ -173,22 +173,6 @@ impl CratePath { let crate_root_path = NamespaceQualifier::new(ir.crate_root_path()); CratePath { crate_ident, crate_root_path, namespace_qualifier } } - - pub fn to_fully_qualified_path(&self, item: Ident) -> FullyQualifiedPath { - let crate_ident = self - .crate_ident - .as_ref() - .cloned() - .unwrap_or_else(|| Ident::new("crate", proc_macro2::Span::call_site())); - FullyQualifiedPath { - start_with_colon2: self.crate_ident.is_some(), - parts: std::iter::once(crate_ident) - .chain(self.crate_root_path.parts_with_snake_case_record_names()) - .chain(self.namespace_qualifier.parts_with_snake_case_record_names()) - .chain(std::iter::once(item)) - .collect(), - } - } } impl ToTokens for CratePath { diff --git a/rs_bindings_from_cc/generate_bindings/lib.rs b/rs_bindings_from_cc/generate_bindings/lib.rs index 874bbd25a..3e2c7b368 100644 --- a/rs_bindings_from_cc/generate_bindings/lib.rs +++ b/rs_bindings_from_cc/generate_bindings/lib.rs @@ -813,31 +813,11 @@ fn make_transmute_abi_type_from_item( cc_name: &str, db: &dyn BindingsGenerator, ) -> Result { - // Rust names are of the form ":: tuples_golden :: NontrivialDrop" - let mut rust_path = rs_name; - let mut start_with_colon2 = false; - if let Some(strip_universal_qualifier) = rust_path.strip_prefix(":: ") { - start_with_colon2 = true; - rust_path = strip_universal_qualifier; - } - let rust_type = FullyQualifiedPath { - start_with_colon2, - parts: rust_path - .split("::") - .map(|ident| { - syn::parse_str::(ident.trim()).map_err(|_| { - anyhow!( - "The type `{ident}` does not parse as an identifier. \ - This may be because it contains template parameters, and \ - bridging such types by value is not yet supported." - ) - }) - }) - .collect::>>()?, - }; + let rust_type = rs_name + .parse() + .map_err(|e| anyhow!("Failed to parse Rust type `{rs_name}` as a TokenStream: {e}"))?; - let cpp_type = make_cpp_type_from_item(item, &cc_name.split("::").collect::>(), db)? - .to_token_stream(); + let cpp_type = make_cpp_type_from_item(item, cc_name, db)?; Ok(CrubitAbiType::Transmute { rust_type, cpp_type }) } @@ -1067,30 +1047,16 @@ fn crubit_abi_type(db: &dyn BindingsGenerator, rs_type_kind: RsTypeKind) -> Resu record.cc_name ); - let rust_type = crate_path - .to_fully_qualified_path(make_rs_ident(record.rs_name.identifier.as_ref())); + let rs_name = make_rs_ident(record.rs_name.identifier.as_ref()); + let rust_type = quote! { #crate_path #rs_name }; // This inlines the logic of code_gen_utils::format_cc_ident and joins the namespace parts, // except that it creates an Ident instead of a TokenStream. code_gen_utils::check_valid_cc_name(&record.cc_name.identifier) .expect("IR should only contain valid C++ types"); - // TODO(okabayashi): File a bug for generalizing "canonical insts". - let cc_name = record.cc_name.identifier.as_ref(); - let cc_name_parts = if cc_name == "std::basic_string_view>" - { - // In the C++ TransmuteAbi, we spell string_view as `std::string_view`. - // In theory we should let Crubit spell the C++ type as - // `std::basic_string_view>`, but `FullyQualifiedPath` - // does not support template arguments right now. It's also the case that since - // Crubit doesn't support templates in general right now, it doesn't make sense to - // support template arguments in `FullyQualifiedPath` yet. - &["std", "string_view"][..] - } else { - &[cc_name][..] - }; let cpp_type = - make_cpp_type_from_item(record.as_ref(), cc_name_parts, db)?.to_token_stream(); + make_cpp_type_from_item(record.as_ref(), record.cc_name.identifier.as_ref(), db)?; Ok(CrubitAbiType::Transmute { rust_type, cpp_type }) } @@ -1421,24 +1387,13 @@ fn strip_leading_colon2(path: &mut &str) -> bool { /// Only to be used in a `CrubitAbiType::Transmute` context. fn make_cpp_type_from_item( item: &impl GenericItem, - cc_name_parts: &[&str], + cc_name: &str, db: &dyn BindingsGenerator, -) -> Result { +) -> Result { let namespace_qualifier = db.ir().namespace_qualifier(item); - let parts = namespace_qualifier - .parts() - .map(AsRef::as_ref) - .chain(cc_name_parts.iter().copied()) - .map(|ident| { - syn::parse_str::(ident).map_err(|_| { - anyhow!( - "The type `{ident}` does not parse as an identifier. \ - This may be because it contains template parameters, and \ - bridging such types by value is not yet supported." - ) - }) - }) - .collect::>>()?; - - Ok(FullyQualifiedPath { start_with_colon2: true, parts }) + let mut namespace_parts = namespace_qualifier.parts().map(|part| make_rs_ident(part)); + let cpp_type = cc_name + .parse::() + .map_err(|e| anyhow!("Failed to parse C++ name: {cc_name}"))?; + Ok(quote! { :: #(#namespace_parts::)* #cpp_type }) } diff --git a/rs_bindings_from_cc/test/golden/BUILD b/rs_bindings_from_cc/test/golden/BUILD index fda0e1c72..8f7740de0 100644 --- a/rs_bindings_from_cc/test/golden/BUILD +++ b/rs_bindings_from_cc/test/golden/BUILD @@ -60,6 +60,15 @@ DEPS = { TAGS = {} +ASPECT_HINTS = { + "composable_bridging_template_type": [ + "//features:wrapper", + ], + "template_inst": [ + "//features:wrapper", + ], +} + rust_bindings_from_cc_cli_flag( name = "disable_source_location_in_doc_comment", flags = "--generate_source_location_in_doc_comment=False", @@ -71,7 +80,7 @@ rust_bindings_from_cc_cli_flag( aspect_hints = [ "//features:supported", ":disable_source_location_in_doc_comment", - ] + (["//features:wrapper"] if name == "template_inst" else []), + ] + (ASPECT_HINTS[name] if name in ASPECT_HINTS else []), deps = [ ((d + "_cc") if d in TESTS else d) for d in (DEPS[name] if name in DEPS else []) @@ -94,6 +103,7 @@ NON_BUILDABLE_TEST = [ # We can't support additional Rust srcs so we can't run the test. "composable_bridging", + "composable_bridging_template_type", ] BUILDABLE_TESTS = [name for name in TESTS if name not in NON_BUILDABLE_TEST] diff --git a/rs_bindings_from_cc/test/golden/composable_bridging_template_type.h b/rs_bindings_from_cc/test/golden/composable_bridging_template_type.h new file mode 100644 index 000000000..52c32588d --- /dev/null +++ b/rs_bindings_from_cc/test/golden/composable_bridging_template_type.h @@ -0,0 +1,31 @@ +// Part of the Crubit project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_COMPOSABLE_BRIDGING_TEMPLATE_TYPE_H_ +#define THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_COMPOSABLE_BRIDGING_TEMPLATE_TYPE_H_ + +template +// clang-format off +struct + [[clang::annotate("crubit_bridge_rust_name", "MyOption")]] + [[clang::annotate("crubit_bridge_abi_rust", "MyOptionAbi")]] + [[clang::annotate("crubit_bridge_abi_cpp", "::crubit::MyOptionAbi")]] +// clang-format on +MyOption { + private: + bool present; + union { + T value; + }; +}; + +// A basic templated type that does nothing fancy. +template +struct Value { + T value; +}; + +MyOption> ReturnsValue(); + +#endif // THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_COMPOSABLE_BRIDGING_TEMPLATE_TYPE_H_ diff --git a/rs_bindings_from_cc/test/golden/composable_bridging_template_type_rs_api.rs b/rs_bindings_from_cc/test/golden/composable_bridging_template_type_rs_api.rs new file mode 100644 index 000000000..dfd993dee --- /dev/null +++ b/rs_bindings_from_cc/test/golden/composable_bridging_template_type_rs_api.rs @@ -0,0 +1,80 @@ +// Part of the Crubit project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Automatically @generated Rust bindings for the following C++ target: +// //rs_bindings_from_cc/test/golden:composable_bridging_template_type_cc + +#![rustfmt::skip] +#![feature(allocator_api, cfg_sanitize, custom_inner_attributes, negative_impls)] +#![allow(stable_features)] +#![no_std] +#![allow(improper_ctypes)] +#![allow(nonstandard_style)] +#![allow(dead_code, unused_mut)] +#![deny(warnings)] + +// Error while generating bindings for class 'MyOption': +// Class templates are not supported yet + +// Error while generating bindings for class 'Value': +// Class templates are not supported yet + +#[inline(always)] +pub fn ReturnsValue() -> crate::MyOption { + unsafe { + ::bridge_rust::unstable_return!(@crate::MyOptionAbi(::bridge_rust::transmute_abi::()),crate::MyOptionAbi<::bridge_rust::TransmuteAbi>,|__return_abi_buffer|{ crate::detail::__rust_thunk___Z12ReturnsValuev(__return_abi_buffer,); }) + } +} + +/// A basic templated type that does nothing fancy. +#[derive(Clone, Copy, ::ctor::MoveAndAssignViaCopy)] +#[repr(C)] +///CRUBIT_ANNOTATE: cpp_type=Value < int > +pub(crate) struct __CcTemplateInst5ValueIiE { + pub value: ::ffi_11::c_int, +} +impl !Send for __CcTemplateInst5ValueIiE {} +impl !Sync for __CcTemplateInst5ValueIiE {} +forward_declare::unsafe_define!( + forward_declare::symbol!("Value < int >"), + crate::__CcTemplateInst5ValueIiE +); + +// Error while generating bindings for constructor 'Value::Value': +// Can't generate bindings for Value::Value, because of missing required features (crubit.rs-features): +// //rs_bindings_from_cc/test/golden:composable_bridging_template_type_cc needs [//features:experimental] for Value::Value (b/248542210: template instantiation of member function cannot reliably get bindings) + +// Error while generating bindings for constructor 'Value::Value': +// Can't generate bindings for Value::Value, because of missing required features (crubit.rs-features): +// //rs_bindings_from_cc/test/golden:composable_bridging_template_type_cc needs [//features:experimental] for Value::Value (b/248542210: template instantiation of member function cannot reliably get bindings) + +// Error while generating bindings for constructor 'Value::Value': +// Can't generate bindings for Value::Value, because of missing required features (crubit.rs-features): +// //rs_bindings_from_cc/test/golden:composable_bridging_template_type_cc needs [//features:experimental] for Value::Value (b/248542210: template instantiation of member function cannot reliably get bindings) + +// Error while generating bindings for function 'Value::operator=': +// Can't generate bindings for Value::operator=, because of missing required features (crubit.rs-features): +// //rs_bindings_from_cc/test/golden:composable_bridging_template_type_cc needs [//features:experimental] for Value::operator= (b/248542210: template instantiation of member function cannot reliably get bindings) + +// Error while generating bindings for function 'Value::operator=': +// Can't generate bindings for Value::operator=, because of missing required features (crubit.rs-features): +// //rs_bindings_from_cc/test/golden:composable_bridging_template_type_cc needs [//features:experimental] for Value::operator= (b/248542210: template instantiation of member function cannot reliably get bindings) + +mod detail { + #[allow(unused_imports)] + use super::*; + unsafe extern "C" { + pub(crate) unsafe fn __rust_thunk___Z12ReturnsValuev( + __return_abi_buffer: *mut ::core::ffi::c_uchar, + ); + } +} + +const _: () = { + assert!(::core::mem::size_of::() == 4); + assert!(::core::mem::align_of::() == 4); + static_assertions::assert_impl_all!(crate::__CcTemplateInst5ValueIiE: Copy,Clone); + static_assertions::assert_not_impl_any!(crate::__CcTemplateInst5ValueIiE: Drop); + assert!(::core::mem::offset_of!(crate::__CcTemplateInst5ValueIiE, value) == 0); +}; diff --git a/rs_bindings_from_cc/test/golden/composable_bridging_template_type_rs_api_impl.cc b/rs_bindings_from_cc/test/golden/composable_bridging_template_type_rs_api_impl.cc new file mode 100644 index 000000000..a60cdc267 --- /dev/null +++ b/rs_bindings_from_cc/test/golden/composable_bridging_template_type_rs_api_impl.cc @@ -0,0 +1,39 @@ +// Part of the Crubit project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Automatically @generated Rust bindings for the following C++ target: +// //rs_bindings_from_cc/test/golden:composable_bridging_template_type_cc + +#include "support/bridge.h" +#include "support/internal/cxx20_backports.h" +#include "support/internal/offsetof.h" +#include "support/internal/sizeof.h" +#include "support/internal/slot.h" + +#include +#include + +// Public headers of the C++ library being wrapped. +#include "rs_bindings_from_cc/test/golden/composable_bridging_template_type.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wthread-safety-analysis" + +extern "C" void __rust_thunk___Z12ReturnsValuev( + unsigned char* __return_abi_buffer) { + ::crubit::Encoder __return_encoder( + ::crubit::MyOptionAbi<::crubit::TransmuteAbi<::Value>>::kSize, + __return_abi_buffer); + ::crubit::MyOptionAbi<::crubit::TransmuteAbi<::Value>>( + ::crubit::TransmuteAbi<::Value>()) + .Encode(ReturnsValue(), __return_encoder); +} + +static_assert((struct MyOption> (*)()) & ::ReturnsValue); + +static_assert(CRUBIT_SIZEOF(struct Value) == 4); +static_assert(alignof(struct Value) == 4); +static_assert(CRUBIT_OFFSET_OF(value, struct Value) == 0); + +#pragma clang diagnostic pop