Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions cc_bindings_from_rs/generate_bindings/format_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Rc<[Ident]>>(),
rust_type: {
let parts = fully_qualified_name.rs_name_parts();
quote! { #(::#parts)* }
},
cpp_type: cpp_type.as_str().parse().expect("Malformed cpp_type annotation"),
},
Expand All @@ -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::<Rc<[Ident]>>(),
rust_type: {
let parts = fully_qualified_name.rs_name_parts();
quote! { #(::#parts)* }
},
cpp_type: fully_qualified_name.format_for_cc(db)?,
}
Expand Down
6 changes: 4 additions & 2 deletions common/crubit_abi_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}")
}),
Expand Down
16 changes: 0 additions & 16 deletions rs_bindings_from_cc/generate_bindings/database/rs_snippet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
73 changes: 14 additions & 59 deletions rs_bindings_from_cc/generate_bindings/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -813,31 +813,11 @@ fn make_transmute_abi_type_from_item(
cc_name: &str,
db: &dyn BindingsGenerator,
) -> Result<CrubitAbiType> {
// 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>(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::<Result<Rc<[Ident]>>>()?,
};
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::<Vec<&str>>(), db)?
.to_token_stream();
let cpp_type = make_cpp_type_from_item(item, cc_name, db)?;

Ok(CrubitAbiType::Transmute { rust_type, cpp_type })
}
Expand Down Expand Up @@ -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<char, std::char_traits<char>>"
{
// 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<char, std::char_traits<char>>`, 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 })
}
Expand Down Expand Up @@ -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<FullyQualifiedPath> {
) -> Result<TokenStream> {
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>(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::<Result<Rc<[Ident]>>>()?;

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::<TokenStream>()
.map_err(|e| anyhow!("Failed to parse C++ name: {cc_name}"))?;
Ok(quote! { :: #(#namespace_parts::)* #cpp_type })
}
12 changes: 11 additions & 1 deletion rs_bindings_from_cc/test/golden/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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 [])
Expand All @@ -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]
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <typename T>
// 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 <typename T>
struct Value {
T value;
};

MyOption<Value<int>> ReturnsValue();

#endif // THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_COMPOSABLE_BRIDGING_TEMPLATE_TYPE_H_
Original file line number Diff line number Diff line change
@@ -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<crate::__CcTemplateInst5ValueIiE> {
unsafe {
::bridge_rust::unstable_return!(@crate::MyOptionAbi(::bridge_rust::transmute_abi::<crate::__CcTemplateInst5ValueIiE>()),crate::MyOptionAbi<::bridge_rust::TransmuteAbi<crate::__CcTemplateInst5ValueIiE>>,|__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<int>::Value<int>':
// Can't generate bindings for Value<int>::Value<int>, because of missing required features (crubit.rs-features):
// //rs_bindings_from_cc/test/golden:composable_bridging_template_type_cc needs [//features:experimental] for Value<int>::Value<int> (b/248542210: template instantiation of member function cannot reliably get bindings)

// Error while generating bindings for constructor 'Value<int>::Value<int>':
// Can't generate bindings for Value<int>::Value<int>, because of missing required features (crubit.rs-features):
// //rs_bindings_from_cc/test/golden:composable_bridging_template_type_cc needs [//features:experimental] for Value<int>::Value<int> (b/248542210: template instantiation of member function cannot reliably get bindings)

// Error while generating bindings for constructor 'Value<int>::Value<int>':
// Can't generate bindings for Value<int>::Value<int>, because of missing required features (crubit.rs-features):
// //rs_bindings_from_cc/test/golden:composable_bridging_template_type_cc needs [//features:experimental] for Value<int>::Value<int> (b/248542210: template instantiation of member function cannot reliably get bindings)

// Error while generating bindings for function 'Value<int>::operator=':
// Can't generate bindings for Value<int>::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<int>::operator= (b/248542210: template instantiation of member function cannot reliably get bindings)

// Error while generating bindings for function 'Value<int>::operator=':
// Can't generate bindings for Value<int>::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<int>::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::<crate::__CcTemplateInst5ValueIiE>() == 4);
assert!(::core::mem::align_of::<crate::__CcTemplateInst5ValueIiE>() == 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);
};
Original file line number Diff line number Diff line change
@@ -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 <cstddef>
#include <memory>

// 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<int>>>::kSize,
__return_abi_buffer);
::crubit::MyOptionAbi<::crubit::TransmuteAbi<::Value<int>>>(
::crubit::TransmuteAbi<::Value<int>>())
.Encode(ReturnsValue(), __return_encoder);
}

static_assert((struct MyOption<Value<int>> (*)()) & ::ReturnsValue);

static_assert(CRUBIT_SIZEOF(struct Value<int>) == 4);
static_assert(alignof(struct Value<int>) == 4);
static_assert(CRUBIT_OFFSET_OF(value, struct Value<int>) == 0);

#pragma clang diagnostic pop
Loading