Thanks to visit codestin.com
Credit goes to github.com

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
28 changes: 19 additions & 9 deletions common/code_gen_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,7 @@ fn parse_any(ident: &str) -> syn::Result<Ident> {
/// Formats a C++ (qualified) identifier. Returns an error when `ident` is a C++
/// reserved keyword or is an invalid identifier.
pub fn format_cc_ident(ident: &str) -> Result<Ident> {
ensure!(!ident.is_empty(), "Empty string is not a valid C++ identifier");
ensure!(
!is_cpp_reserved_keyword(ident),
"`{ident}` is a C++ reserved keyword and can't be used as a C++ identifier",
);
check_valid_cc_name(ident)?;
// Explicitly mapping the error via `anyhow!`, because `LexError` is not `Sync`
// (required for `anyhow::Error` to implement `From<LexError>`) and
// therefore we can't just use `?`.
Expand Down Expand Up @@ -378,12 +374,26 @@ impl NamespaceQualifier {

/// Returns `foo::bar::baz::` (reporting errors for C++ keywords).
pub fn format_for_cc(&self) -> Result<TokenStream> {
let namespace_cc_idents = self.cc_idents()?;
Ok(quote! { #(#namespace_cc_idents::)* })
let mut path = quote! {};
for namespace in &self.namespaces {
let namespace = format_cc_ident(namespace)?;
path.extend(quote! { #namespace :: });
}
for (rs_name, cc_name) in &self.nested_records {
let cc_name = format_cc_type_name(&cc_name)?;
path.extend(quote! { #cc_name ::});
}
Ok(path)
}

pub fn cc_idents(&self) -> Result<Vec<Ident>> {
self.parts().map(|ns| format_cc_ident(ns)).collect()
/// Returns `foo::bar::baz::` (never reporting errors).
pub fn format_for_cc_debug(&self) -> String {
let mut path = String::new();
for part in self.parts() {
path.push_str(part);
path.push_str("::");
}
path
}
}

Expand Down
65 changes: 20 additions & 45 deletions rs_bindings_from_cc/generate_bindings/cpp_type_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,73 +111,48 @@ pub fn format_cpp_type_inner(
> #ptr
})
}
RsTypeKind::IncompleteRecord { incomplete_record, .. } => {
cpp_type_name_for_item(&Item::IncompleteRecord(Rc::clone(incomplete_record)), ir)
}
RsTypeKind::IncompleteRecord { incomplete_record, .. } => tagless_cpp_type_name_for_item(
&Item::IncompleteRecord(Rc::clone(incomplete_record)),
ir,
),
RsTypeKind::Record { record, .. } => cpp_type_name_for_record(record, ir),
RsTypeKind::Enum { enum_, .. } => cpp_type_name_for_item(&Item::Enum(Rc::clone(enum_)), ir),
RsTypeKind::Enum { enum_, .. } => {
tagless_cpp_type_name_for_item(&Item::Enum(Rc::clone(enum_)), ir)
}
RsTypeKind::TypeAlias { type_alias, .. } => {
cpp_type_name_for_item(&Item::TypeAlias(Rc::clone(type_alias)), ir)
tagless_cpp_type_name_for_item(&Item::TypeAlias(Rc::clone(type_alias)), ir)
}
RsTypeKind::Primitive(primitive) => Ok(quote! { #primitive }),
RsTypeKind::BridgeType { original_type, .. } => cpp_type_name_for_record(original_type, ir),
RsTypeKind::ExistingRustType(existing_rust_type) => {
cpp_type_name_for_item(&Item::ExistingRustType(Rc::clone(existing_rust_type)), ir)
}
RsTypeKind::ExistingRustType(existing_rust_type) => tagless_cpp_type_name_for_item(
&Item::ExistingRustType(Rc::clone(existing_rust_type)),
ir,
),
RsTypeKind::C9Co { original_type, .. } => cpp_type_name_for_record(original_type, ir),
}
}

/// Returns the fully-qualified name for an item, not including the type tag.
pub fn tagless_cpp_type_name_for_item(item: &ir::Item, ir: &IR) -> Result<TokenStream> {
if let ir::Item::Record(record) = item {
cpp_tagless_type_name_for_record(record, ir)
} else {
cpp_type_name_for_item(item, ir)
}
}

/// Returns the fully qualified name for an item.
/// Returns the fully qualified name for an item (not including type tags).
///
/// For example, for `namespace x { struct Y { using X = int; }; }`, the name
/// for `X` is `x::Y::X`.
fn cpp_type_name_for_item(item: &ir::Item, ir: &IR) -> Result<TokenStream> {
/// Returns the namespace / class qualifiers necessary to access the item.
///
/// For example, for `namespace x { struct Y { using X = int; }; }`, the prefix
/// for `X` is `x::Y::`.
fn cpp_qualified_path_prefix(item: &ir::Item, ir: &ir::IR) -> Result<TokenStream> {
let Some(parent) = item.enclosing_item_id() else {
return Ok(quote! {});
};
let parent: &ir::Item = ir.find_decl(parent)?;
match parent {
ir::Item::Namespace(_) => Ok(ir.namespace_qualifier(item).format_for_cc()?),
ir::Item::Record(r) => {
let name = cpp_tagless_type_name_for_record(r, ir)?;
Ok(quote! {#name ::})
}
_ => bail!("Unexpected enclosing item: {item:?}"),
}
}

pub fn tagless_cpp_type_name_for_item(item: &ir::Item, ir: &IR) -> Result<TokenStream> {
match item {
Item::IncompleteRecord(incomplete_record) => {
let ident = expect_format_cc_type_name(incomplete_record.cc_name.identifier.as_ref());
let namespace_qualifier = ir.namespace_qualifier(incomplete_record).format_for_cc()?;
let tag_kind = incomplete_record.record_type;
Ok(quote! { #tag_kind #namespace_qualifier #ident })
Ok(quote! { #namespace_qualifier #ident })
}
Item::Record(record) => cpp_type_name_for_record(record, ir),
Item::Record(record) => cpp_tagless_type_name_for_record(record, ir),
Item::Enum(enum_) => {
let ident = expect_format_cc_type_name(&enum_.rs_name.identifier);
let qualifier = cpp_qualified_path_prefix(item, ir)?;
Ok(quote! { #qualifier #ident })
let namespace_qualifier = ir.namespace_qualifier(item).format_for_cc()?;
Ok(quote! { #namespace_qualifier #ident })
}
Item::TypeAlias(type_alias) => {
let ident = expect_format_cc_type_name(&type_alias.cc_name.identifier);
let qualifier = cpp_qualified_path_prefix(item, ir)?;
Ok(quote! { #qualifier #ident })
let namespace_qualifier = ir.namespace_qualifier(item).format_for_cc()?;
Ok(quote! { #namespace_qualifier #ident })
}
Item::ExistingRustType(existing_rust_type) => existing_rust_type
.cc_name
Expand Down
26 changes: 14 additions & 12 deletions rs_bindings_from_cc/generate_bindings/generate_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -865,26 +865,25 @@ fn api_func_shape_for_constructor(
/// become a `RvalueReference<'_, T>`.
///
/// Returns:
///
/// * `Err(_)`: something went wrong importing this function.
/// * `Ok(None)`: the function imported as "nothing". (For example, a defaulted
/// * `None`: the function imported as "nothing". (For example, a defaulted
/// destructor might be mapped to no `Drop` impl at all.)
/// * `Ok((func_name, impl_kind))`: The function name and ImplKind.
/// * `(func_name, impl_kind)`: The function name and ImplKind.
fn api_func_shape(
db: &dyn BindingsGenerator,
func: &Func,
param_types: &mut [RsTypeKind],
errors: &Errors,
) -> Option<(Ident, ImplKind)> {
let ir = db.ir();
let maybe_record = match ir.record_for_member_func(func).map(<&Rc<Record>>::try_from) {
let maybe_record = match func.enclosing_item_id.map(|id| ir.find_untyped_decl(id)) {
None => None,
Some(Ok(record)) => Some(record),
// Functions whose record was replaced with some other IR Item type are ignored.
// This occurs for instance if you use crubit_internal_rust_type: member functions defined
// out-of-line, such as implicitly generated constructors, will still be present in the IR,
// but should be ignored.
Some(Err(_)) => return None,
Some(ir::Item::Namespace(_)) => None,
Some(ir::Item::Record(record)) => Some(record),
// If the record was replaced by an existing Rust type using `crubit_internal_rust_type`,
// don't generate any bindings for its functions. (That can't work!)
Some(ir::Item::ExistingRustType(_)) => return None,
// (This case should be impossible.)
Some(_) => return None,
};

if is_friend_of_record_not_visible_by_adl(db, func, param_types) {
Expand Down Expand Up @@ -2256,7 +2255,10 @@ fn has_copy_assignment_operator_from_const_reference(
if first_param_type.is_err() {
return false;
};
let Some(Item::Record(record)) = db.ir().record_for_member_func(copy_constructor) else {
let Some(parent_id) = copy_constructor.enclosing_item_id else {
return false;
};
let Ok(record) = db.ir().find_decl::<Rc<Record>>(parent_id) else {
return false;
};
record
Expand Down
114 changes: 44 additions & 70 deletions rs_bindings_from_cc/generate_bindings/generate_function_thunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,9 @@ pub fn can_skip_cc_thunk(db: &dyn BindingsGenerator, func: &Func) -> bool {
// In terms of runtime performance, since this only occurs for virtual function
// calls, which are already slow, it may not be such a big deal. We can
// benchmark it later. :)
if let Some(meta) = &func.member_func_metadata {
if let Some(inst_meta) = &meta.instance_method_metadata {
if inst_meta.is_virtual {
return false;
}
if let Some(inst_meta) = &func.instance_method_metadata {
if inst_meta.is_virtual {
return false;
}
}
// ## Custom calling convention requires a thunk.
Expand Down Expand Up @@ -260,44 +258,32 @@ fn generate_function_assertation_for_identifier(
let ir = db.ir();

let fn_ident = expect_format_cc_ident(&id.identifier);
let path_to_func = ir.namespace_qualifier(func).format_for_cc()?;
let implementation_function = quote! { :: #path_to_func #fn_ident };
let method_qualification;
let implementation_function;
let member_function_prefix;
let func_params;
if let Some(meta) = func.member_func_metadata.as_ref() {
let record: &Rc<Record> = ir.find_decl(meta.record_id)?;
let record_ident = expect_format_cc_type_name(record.cc_name.identifier.as_ref());
let namespace_qualifier = ir.namespace_qualifier(record).format_for_cc()?;
if let Some(instance_method_metadata) = meta.instance_method_metadata.as_ref() {
let const_qualifier = if instance_method_metadata.is_const {
quote! {const}
} else {
quote! {}
};

method_qualification = match instance_method_metadata.reference {
ir::ReferenceQualification::Unqualified => const_qualifier,
ir::ReferenceQualification::LValue => {
quote! { #const_qualifier & }
}
ir::ReferenceQualification::RValue => {
quote! { #const_qualifier && }
}
};
implementation_function = quote! { #namespace_qualifier #record_ident :: #fn_ident };
member_function_prefix = quote! { :: #namespace_qualifier #record_ident :: };
// The first parameter of instance methods is `this`.
func_params = &func.params[1..];
if let Some(instance_method_metadata) = &func.instance_method_metadata {
let const_qualifier = if instance_method_metadata.is_const {
quote! {const}
} else {
method_qualification = quote! {};
implementation_function = quote! { #namespace_qualifier #record_ident :: #fn_ident };
member_function_prefix = quote! {};
func_params = &func.params[..];
}
quote! {}
};

method_qualification = match instance_method_metadata.reference {
ir::ReferenceQualification::Unqualified => const_qualifier,
ir::ReferenceQualification::LValue => {
quote! { #const_qualifier & }
}
ir::ReferenceQualification::RValue => {
quote! { #const_qualifier && }
}
};
member_function_prefix = path_to_func;
// The first parameter of instance methods is `this`.
func_params = &func.params[1..];
} else {
let namespace_qualifier = ir.namespace_qualifier(func).format_for_cc()?;
method_qualification = quote! {};
implementation_function = quote! { #namespace_qualifier #fn_ident };
member_function_prefix = quote! {};
func_params = &func.params[..];
}
Expand Down Expand Up @@ -394,22 +380,11 @@ pub fn generate_function_thunk_impl(
}
UnqualifiedIdentifier::Identifier(id) => {
let fn_ident = expect_format_cc_ident(&id.identifier);
match func.member_func_metadata.as_ref() {
Some(meta) => {
if meta.instance_method_metadata.is_some() {
quote! { #fn_ident }
} else {
let record: &Rc<Record> = ir.find_decl(meta.record_id)?;
let record_name =
expect_format_cc_type_name(record.cc_name.identifier.as_ref());
let namespace_qualifier = ir.namespace_qualifier(record).format_for_cc()?;
quote! { #namespace_qualifier #record_name :: #fn_ident }
}
}
None => {
let namespace_qualifier = ir.namespace_qualifier(func).format_for_cc()?;
quote! { #namespace_qualifier #fn_ident }
}
let namespace_qualifier = ir.namespace_qualifier(func).format_for_cc()?;
if func.instance_method_metadata.is_some() {
quote! {#fn_ident}
} else {
quote! { #namespace_qualifier #fn_ident }
}
}
// Use `destroy_at` to avoid needing to spell out the class name. Destructor identiifers
Expand All @@ -419,15 +394,16 @@ pub fn generate_function_thunk_impl(
// using destroy_at, we avoid needing to determine or remember what the correct spelling
// is. Similar arguments apply to `construct_at`.
UnqualifiedIdentifier::Constructor => {
if let Some(meta) = func.member_func_metadata.as_ref() {
let record: &Rc<Record> = ir.find_decl(meta.record_id)?;
if is_copy_constructor(func, record.id)
&& record.copy_constructor == SpecialMemberFunc::Unavailable
{
bail!(
"Would use an unavailable copy constructor for {}",
record.cc_name.identifier.as_ref()
);
if let Some(parent_id) = func.enclosing_item_id {
if let Ok(record) = ir.find_decl::<Rc<Record>>(parent_id) {
if is_copy_constructor(func, record.id)
&& record.copy_constructor == SpecialMemberFunc::Unavailable
{
bail!(
"Would use an unavailable copy constructor for {}",
record.cc_name.identifier.as_ref()
);
}
}
}
quote! { crubit::construct_at }
Expand Down Expand Up @@ -565,14 +541,12 @@ pub fn generate_function_thunk_impl(
return_type_cpp_spelling.clone()
};

let mut this_ref_qualification =
func.member_func_metadata.as_ref().and_then(|meta| match &func.rs_name {
UnqualifiedIdentifier::Constructor | UnqualifiedIdentifier::Destructor => None,
UnqualifiedIdentifier::Identifier(_) | UnqualifiedIdentifier::Operator(_) => meta
.instance_method_metadata
.as_ref()
.map(|instance_method| instance_method.reference),
});
let mut this_ref_qualification = match &func.rs_name {
UnqualifiedIdentifier::Constructor | UnqualifiedIdentifier::Destructor => None,
UnqualifiedIdentifier::Identifier(_) | UnqualifiedIdentifier::Operator(_) => {
func.instance_method_metadata.as_ref().map(|meta| meta.reference)
}
};
if func.cc_name.is_constructor() {
this_ref_qualification = None;
}
Expand Down
19 changes: 7 additions & 12 deletions rs_bindings_from_cc/importers/function.cc
Original file line number Diff line number Diff line change
Expand Up @@ -507,33 +507,28 @@ std::optional<IR::Item> FunctionDeclImporter::Import(
errors.Add(FormattedError::Static("Inline function is not defined"));
}

std::optional<MemberFuncMetadata> member_func_metadata;
std::optional<InstanceMethodMetadata> instance_metadata;
if (auto* method_decl =
clang::dyn_cast<clang::CXXMethodDecl>(function_decl)) {
std::optional<MemberFuncMetadata::InstanceMethodMetadata> instance_metadata;
if (method_decl->isInstance()) {
MemberFuncMetadata::ReferenceQualification reference;
InstanceMethodMetadata::ReferenceQualification reference;
switch (method_decl->getRefQualifier()) {
case clang::RQ_LValue:
reference = MemberFuncMetadata::kLValue;
reference = InstanceMethodMetadata::kLValue;
break;
case clang::RQ_RValue:
reference = MemberFuncMetadata::kRValue;
reference = InstanceMethodMetadata::kRValue;
break;
case clang::RQ_None:
reference = MemberFuncMetadata::kUnqualified;
reference = InstanceMethodMetadata::kUnqualified;
break;
}
instance_metadata = MemberFuncMetadata::InstanceMethodMetadata{
instance_metadata = InstanceMethodMetadata{
.reference = reference,
.is_const = method_decl->isConst(),
.is_virtual = method_decl->isVirtual(),
};
}

member_func_metadata = MemberFuncMetadata{
.record_id = ictx_.GenerateItemId(method_decl->getParent()),
.instance_method_metadata = instance_metadata};
}

if (!errors.error_set.empty()) {
Expand Down Expand Up @@ -618,7 +613,7 @@ std::optional<IR::Item> FunctionDeclImporter::Import(
.params = std::move(params),
.lifetime_params = std::move(lifetime_params),
.is_inline = is_inline,
.member_func_metadata = std::move(member_func_metadata),
.instance_method_metadata = std::move(instance_metadata),
.is_extern_c = function_decl->isExternC(),
.is_noreturn = function_decl->isNoReturn(),
.is_variadic = function_decl->isVariadic(),
Expand Down
Loading
Loading