From baeefcbf817878d089de40abf4ccd0def85291ba Mon Sep 17 00:00:00 2001 From: Ethan Smith Date: Fri, 6 Mar 2026 17:57:11 -0800 Subject: [PATCH] Generate bindings to implementations of generic traits. Implementations of generic traits will receive bindings if their implementation is fully monomorphic. Traits with constant parameters still will not receive bindings. PiperOrigin-RevId: 879900064 --- .../generate_bindings/generate_function.rs | 62 ++++- .../generate_struct_and_union.rs | 2 +- cc_bindings_from_rs/generate_bindings/lib.rs | 175 +++++++++----- cc_bindings_from_rs/test/traits/BUILD | 34 +++ .../test/traits/generic_traits.rs | 55 +++++ .../test/traits/generic_traits_cc_api.h | 217 ++++++++++++++++++ .../test/traits/generic_traits_cc_api_impl.rs | 47 ++++ .../test/traits/generic_traits_test.cc | 42 ++++ cc_bindings_from_rs/test/traits/traits.rs | 10 - .../test/traits/traits_cc_api.h | 66 +++--- .../test/traits/traits_test.cc | 1 - 11 files changed, 597 insertions(+), 114 deletions(-) create mode 100644 cc_bindings_from_rs/test/traits/generic_traits.rs create mode 100644 cc_bindings_from_rs/test/traits/generic_traits_cc_api.h create mode 100644 cc_bindings_from_rs/test/traits/generic_traits_cc_api_impl.rs create mode 100644 cc_bindings_from_rs/test/traits/generic_traits_test.cc diff --git a/cc_bindings_from_rs/generate_bindings/generate_function.rs b/cc_bindings_from_rs/generate_bindings/generate_function.rs index 8d5004175..496eb5c1c 100644 --- a/cc_bindings_from_rs/generate_bindings/generate_function.rs +++ b/cc_bindings_from_rs/generate_bindings/generate_function.rs @@ -29,7 +29,7 @@ use quote::quote; use rustc_hir::attrs::AttributeKind; use rustc_hir::{self as hir, def::DefKind}; use rustc_middle::mir::Mutability; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt}; use rustc_span::def_id::DefId; use rustc_span::symbol::Symbol; use std::collections::BTreeSet; @@ -697,6 +697,54 @@ fn get_function_cc_name(db: &BindingsGenerator, def_id: DefId) -> Result .context("Error formatting function name") } +fn format_trait_ref_for_cc<'tcx>( + db: &BindingsGenerator<'tcx>, + trait_ref: &TraitRef<'tcx>, +) -> Result> { + let trait_name = db + .symbol_canonical_name(trait_ref.def_id) + .and_then(|fully_qualified_name| fully_qualified_name.format_for_cc(db).ok()) + .expect("Generated trait method for a trait with an invalid cc name"); + let mut trait_args = trait_ref.args[1..].iter().filter_map(|arg| arg.as_type()).peekable(); + let mut prereqs = CcPrerequisites::default(); + let tokens = if trait_args.peek().is_none() { + quote! { #trait_name } + } else { + let arg_tokens = trait_args + .map(|ty_arg| { + Ok(db.format_ty_for_cc(ty_arg, TypeLocation::Other)?.into_tokens(&mut prereqs)) + }) + .collect::>>()?; + quote! { #trait_name<#(#arg_tokens),*> } + }; + Ok(CcSnippet { prereqs, tokens }) +} + +fn format_trait_ref_for_rs<'tcx>( + db: &BindingsGenerator<'tcx>, + trait_ref: &TraitRef<'tcx>, +) -> Result { + let trait_name = db + .symbol_canonical_name(trait_ref.def_id) + .map(|fully_qualified_name| fully_qualified_name.format_for_rs()) + .expect("Generated trait method for a trait with an invalid rs name"); + let mut trait_args = trait_ref.args[1..].iter().filter_map(|arg| arg.as_type()).peekable(); + if trait_args.peek().is_none() { + Ok(quote! { #trait_name }) + } else { + let arg_tokens = trait_args + .map(|ty_arg| { + let static_ty_arg = crate::generate_function_thunk::replace_all_regions_with_static( + db.tcx(), + ty_arg, + ); + db.format_ty_for_rs(static_ty_arg) + }) + .collect::>>()?; + Ok(quote! { #trait_name<#(#arg_tokens),*> }) + } +} + /// Implementation of `BindingsGenerator::generate_function`. pub fn generate_function<'tcx>( db: &BindingsGenerator<'tcx>, @@ -946,15 +994,14 @@ pub fn generate_function<'tcx>( let decl_name = trait_ref .as_ref() .map(|trait_ref| { - let trait_name = db - .symbol_canonical_name(trait_ref.def_id) - .and_then(|fully_qualified_name| fully_qualified_name.format_for_cc(db).ok()) - .expect("Generated trait method for a trait with an invalid rust name"); let struct_name = struct_name .as_ref() .and_then(|fully_qualified_name| fully_qualified_name.format_for_cc(db).ok()) .expect("Generated trait method for an ADT with an invalid rust name"); - quote! { rs_std :: impl <#struct_name, #trait_name> :: #bracketed_decl_name } + let trait_name_with_args = format_trait_ref_for_cc(db, trait_ref) + .expect("Implementation of trait containing invalid type requested. Caller should have verified type arguments were valid.") + .into_tokens(&mut prereqs); + quote! { rs_std :: impl <#struct_name, #trait_name_with_args> :: #bracketed_decl_name } }) .or_else(|| { struct_name.as_ref().map(|fully_qualified_name| { @@ -993,7 +1040,8 @@ pub fn generate_function<'tcx>( .map(|fully_qualified_name| fully_qualified_name.format_for_rs()) .expect("Generated trait method for an ADT with an invalid rust name"); let fn_name = make_rs_ident(unqualified_rust_fn_name.as_str()); - quote! { <#struct_name as #trait_name>::#fn_name } + let trait_name_with_args = format_trait_ref_for_rs(db, trait_ref).expect("Implementation of trait containing invalid type requested. Caller should have verified type arguments were valid."); + quote! { <#struct_name as #trait_name_with_args>::#fn_name } }) // Inherent method .or_else(|| struct_name.as_ref().map(|struct_name| { diff --git a/cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs b/cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs index 51c094402..af4ba8685 100644 --- a/cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs +++ b/cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs @@ -38,7 +38,7 @@ use std::collections::{BTreeSet, HashMap, HashSet}; use std::iter::once; use std::rc::Rc; -fn has_type_or_const_vars() -> TypeFlags { +pub(crate) fn has_type_or_const_vars() -> TypeFlags { TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_CT_PARAM | TypeFlags::HAS_TY_INFER diff --git a/cc_bindings_from_rs/generate_bindings/lib.rs b/cc_bindings_from_rs/generate_bindings/lib.rs index ab9594256..9abcea1b6 100644 --- a/cc_bindings_from_rs/generate_bindings/lib.rs +++ b/cc_bindings_from_rs/generate_bindings/lib.rs @@ -33,7 +33,7 @@ use crate::generate_function::{generate_function, must_use_attr_of}; use crate::generate_function_thunk::{generate_trait_thunks, TraitThunks}; use crate::generate_struct_and_union::{ adt_needs_bindings, cpp_enum_cpp_underlying_type, from_trait_impls_by_argument, generate_adt, - generate_adt_core, generate_associated_item, scalar_value_to_string, + generate_adt_core, generate_associated_item, has_type_or_const_vars, scalar_value_to_string, }; use arc_anyhow::{Context, Error, Result}; use code_gen_utils::{format_cc_includes, CcConstQualifier, CcInclude, NamespaceQualifier}; @@ -58,7 +58,7 @@ use rustc_abi::{AddressSpace, BackendRepr, Integer, Primitive, Scalar}; use rustc_hir::def::{DefKind, Res}; use rustc_middle::metadata::{ModChild, Reexport}; use rustc_middle::mir::ConstValue; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_span::symbol::{sym, Symbol}; use std::cmp::Ordering; @@ -862,12 +862,13 @@ fn supported_traits(db: &BindingsGenerator<'_>) -> Rc<[DefId]> { && crate_name.as_str() != "alloc"; let generics = tcx.generics_of(*trait_id); - // TODO: b/259749095 - Support generics in Traits. - // Traits will have a single parameter for the self type which is allowed. - let no_generic_args = (generics.has_self - && generics.own_params.iter().filter(|param| param.kind.is_ty_or_const()).count() - == 1) - || !generics.requires_monomorphization(tcx); + // Traits do not support const generics. + let no_generic_args = generics + .own_params + .iter() + .filter(|param| matches!(param.kind, GenericParamDefKind::Const { .. })) + .count() + == 0; let is_exposed_trait = db.symbol_canonical_name(*trait_id).is_some(); // We might want to explicitly omit certain marker traits here that are already handled by the bindings for structs/enums (Copy, Clone, Default, etc.). @@ -896,12 +897,33 @@ fn generate_trait<'tcx>( let rs_type = canonical_name.format_for_rs().to_string(); let attributes = vec![quote! {CRUBIT_INTERNAL_RUST_TYPE(#rs_type)}]; + let tcx = db.tcx(); + let generics = tcx.generics_of(trait_id); + let own_params: Vec<_> = generics + .own_params + .iter() + .filter(|param| matches!(param.kind, GenericParamDefKind::Type { .. })) + .collect(); + let trait_params = if generics.has_self { &own_params[1..] } else { &own_params[..] }; + + let (template_prefix, trait_name_with_args) = if trait_params.is_empty() { + (quote! {}, quote! { #trait_name }) + } else { + let template_params = trait_params.iter().enumerate().map(|(i, _)| { + let param_name = format_ident!("T{}", i); + quote! { typename #param_name } + }); + let template_args = trait_params.iter().enumerate().map(|(i, _)| format_ident!("T{}", i)); + (quote! { template <#(#template_params),*> }, quote! { #trait_name<#(#template_args),*> }) + }; + let main_api = CcSnippet::with_include( quote! { __NEWLINE__ #doc_comment + #template_prefix struct #(#attributes)* #trait_name { template - using impl = rs_std::impl; + using impl = rs_std::impl; }; __NEWLINE__ }, @@ -1659,62 +1681,95 @@ fn generate_trait_impls<'a, 'tcx>( .map(move |impl_def_id| (adt_cc_name.clone(), trait_def_id, impl_def_id)) }) }) - .map(move |(adt_cc_name, trait_def_id, impl_def_id)| { - let canonical_name = db.symbol_canonical_name(trait_def_id).expect( - "symbol_canonical_name was unexpectedly called on a trait without a canonical name", - ); - let trait_name = canonical_name.format_for_cc(db).map_err(|err| (impl_def_id, err))?; - let mut prereqs = CcPrerequisites::default(); - if trait_def_id.krate == db.source_crate_num() { - prereqs.defs.insert(trait_def_id); - } else { - let other_crate_name = tcx.crate_name(trait_def_id.krate); - let crate_name_to_include_paths = db.crate_name_to_include_paths(); - let includes = crate_name_to_include_paths - .get(other_crate_name.as_str()) - .ok_or_else(|| { - let trait_name = tcx.def_path_str(trait_def_id); - ( - impl_def_id, - anyhow!( - "Trait `{trait_name}` comes from the `{other_crate_name}` crate, \ - but no `--crate-header` was specified for this crate" - ), - ) - })?; - prereqs.includes.extend(includes.iter().cloned()); - } + .map( + move |(adt_cc_name, trait_def_id, impl_def_id)| -> Result { + let trait_header = tcx.impl_trait_header(impl_def_id); + #[rustversion::before(2025-10-17)] + let trait_header = trait_header.expect("Trait impl should have a trait header"); + let trait_ref = trait_header.trait_ref.instantiate_identity(); + + let canonical_name = db.symbol_canonical_name(trait_def_id).expect( + "symbol_canonical_name was unexpectedly called on a trait without a canonical name", + ); + let trait_name = + canonical_name.format_for_cc(db).map_err(|err| (impl_def_id, err))?; - let mut member_function_names = HashSet::new(); - let assoc_items: ApiSnippets = tcx - .associated_items(impl_def_id) - .in_definition_order() - .flat_map(|assoc_item| { - generate_associated_item(db, assoc_item, &mut member_function_names) - }) - .collect(); + let mut prereqs = CcPrerequisites::default(); + let trait_args: Vec<_> = trait_ref + .args + .iter() + // Skip self type. + .skip(1) + .filter_map(|arg| arg.as_type()) + .map(|arg| { + if arg.flags().contains(has_type_or_const_vars()) { + return Err((impl_def_id, anyhow!("Implementation of traits must specify all types to receive bindings."))); + } + db.format_ty_for_cc(arg, TypeLocation::Other) + .map(|snippet| snippet.into_tokens(&mut prereqs)) + .map_err(|err| (impl_def_id, err)) + }) + .collect::, _>>()?; - let main_api = assoc_items.main_api.into_tokens(&mut prereqs); - prereqs.includes.insert(db.support_header("rs_std/traits.h")); + let type_args = if trait_args.is_empty() { + quote! {} + } else { + quote! { <#(#trait_args),*> } + }; - Ok(ApiSnippets { - main_api: CcSnippet { - tokens: quote! { - __NEWLINE__ - template<> - struct rs_std::impl<#adt_cc_name, #trait_name> { - static constexpr bool kIsImplemented = true; + let trait_name_with_args = quote! { #trait_name #type_args }; - #main_api - }; - __NEWLINE__ + if trait_def_id.krate == db.source_crate_num() { + prereqs.defs.insert(trait_def_id); + } else { + let other_crate_name = tcx.crate_name(trait_def_id.krate); + let crate_name_to_include_paths = db.crate_name_to_include_paths(); + let includes = crate_name_to_include_paths + .get(other_crate_name.as_str()) + .ok_or_else(|| { + let trait_name = tcx.def_path_str(trait_def_id); + ( + impl_def_id, + anyhow!( + "Trait `{trait_name}` comes from the `{other_crate_name}` crate, \ + but no `--crate-header` was specified for this crate" + ), + ) + })?; + prereqs.includes.extend(includes.iter().cloned()); + } + + let mut member_function_names = HashSet::new(); + let assoc_items: ApiSnippets = tcx + .associated_items(impl_def_id) + .in_definition_order() + .flat_map(|assoc_item| { + generate_associated_item(db, assoc_item, &mut member_function_names) + }) + .collect(); + + let main_api = assoc_items.main_api.into_tokens(&mut prereqs); + prereqs.includes.insert(db.support_header("rs_std/traits.h")); + + Ok(ApiSnippets { + main_api: CcSnippet { + tokens: quote! { + __NEWLINE__ + template<> + struct rs_std::impl<#adt_cc_name, #trait_name_with_args> { + static constexpr bool kIsImplemented = true; + + #main_api + }; + __NEWLINE__ + }, + prereqs, }, - prereqs, - }, - cc_details: assoc_items.cc_details, - rs_details: assoc_items.rs_details, - }) - }) + cc_details: assoc_items.cc_details, + rs_details: assoc_items.rs_details, + }) + }, + ) .map(|results_snippets| { results_snippets.unwrap_or_else(|(def_id, err)| { generate_unsupported_def(db, def_id, err).into_main_api() diff --git a/cc_bindings_from_rs/test/traits/BUILD b/cc_bindings_from_rs/test/traits/BUILD index 335f58a2d..355a4d063 100644 --- a/cc_bindings_from_rs/test/traits/BUILD +++ b/cc_bindings_from_rs/test/traits/BUILD @@ -50,3 +50,37 @@ crubit_cc_test( "//testing/base/public:gunit_main", ], ) + +rust_library( + name = "generic_traits", + srcs = ["generic_traits.rs"], + aspect_hints = [ + "//features:experimental", + ], + proc_macro_deps = [ + "//support:crubit_annotate", + ], +) + +cc_bindings_from_rust( + name = "generic_traits_cc_api", + testonly = 1, + crate = ":generic_traits", +) + +golden_test( + name = "generic_traits_golden_test", + basename = "generic_traits", + golden_h = "generic_traits_cc_api.h", + golden_rs = "generic_traits_cc_api_impl.rs", + rust_library = "generic_traits", +) + +crubit_cc_test( + name = "generic_traits_test", + srcs = ["generic_traits_test.cc"], + deps = [ + ":generic_traits_cc_api", + "//testing/base/public:gunit_main", + ], +) diff --git a/cc_bindings_from_rs/test/traits/generic_traits.rs b/cc_bindings_from_rs/test/traits/generic_traits.rs new file mode 100644 index 000000000..ed7b437fa --- /dev/null +++ b/cc_bindings_from_rs/test/traits/generic_traits.rs @@ -0,0 +1,55 @@ +// 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 + +pub trait TraitWithGeneric { + fn foo(&self, t: T) -> T; +} + +pub struct StructGeneric { + pub x: i32, +} + +impl StructGeneric { + pub fn new(x: i32) -> Self { + Self { x } + } +} + +// This implementation specifies every type argument (i32), so it should receive bindings. +impl TraitWithGeneric for StructGeneric { + fn foo(&self, t: i32) -> i32 { + self.x + t + } +} + +pub trait TraitWithTwoGenerics { + fn bar(&self, t: T, u: U) -> i32; +} + +// Specifies both -> should bind. +impl TraitWithTwoGenerics for StructGeneric { + fn bar(&self, t: i32, u: i32) -> i32 { + self.x + t + u + } +} + +pub struct AnotherStruct { + pub y: i32, +} + +// Specifies only one -> should NOT bind. +impl TraitWithTwoGenerics for AnotherStruct { + fn bar(&self, t: i32, _u: U) -> i32 { + self.y + t + } +} + +// Trait with a constant parameter should NOT receive bindings. +pub trait TraitWithConst { + fn baz(&self) -> usize { + N + } +} + +impl TraitWithConst<42> for StructGeneric {} diff --git a/cc_bindings_from_rs/test/traits/generic_traits_cc_api.h b/cc_bindings_from_rs/test/traits/generic_traits_cc_api.h new file mode 100644 index 000000000..f5dc505ba --- /dev/null +++ b/cc_bindings_from_rs/test/traits/generic_traits_cc_api.h @@ -0,0 +1,217 @@ +// 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 C++ bindings for the following Rust crate: +// generic_traits_golden +// Features: assume_lifetimes, assume_this_lifetimes, callables, +// check_default_initialized, experimental, fmt, supported, unsafe_view, wrapper + +// clang-format off +#ifndef THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_TRAITS_GENERIC_TRAITS_GOLDEN +#define THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_TRAITS_GENERIC_TRAITS_GOLDEN + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +#pragma clang diagnostic ignored "-Wunused-private-field" +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#include "support/annotations_internal.h" +#include "support/internal/slot.h" +#include "support/rs_std/traits.h" + +#include +#include +#include +#include + +namespace generic_traits { + +// Generated from: +// cc_bindings_from_rs/test/traits/generic_traits.rs;l=37 +struct CRUBIT_INTERNAL_RUST_TYPE( + ":: generic_traits_golden :: AnotherStruct") alignas(4) + [[clang::trivial_abi]] AnotherStruct final { + public: + // `generic_traits_golden::AnotherStruct` doesn't implement the `Default` + // trait + AnotherStruct() = delete; + + // No custom `Drop` impl and no custom "drop glue" required + ~AnotherStruct() = default; + AnotherStruct(AnotherStruct&&) = default; + ::generic_traits::AnotherStruct& operator=(AnotherStruct&&) = default; + + // `generic_traits_golden::AnotherStruct` doesn't implement the `Clone` trait + AnotherStruct(const AnotherStruct&) = delete; + AnotherStruct& operator=(const AnotherStruct&) = delete; + AnotherStruct(::crubit::UnsafeRelocateTag, AnotherStruct&& value) { + memcpy(this, &value, sizeof(value)); + } + union { + // Generated from: + // cc_bindings_from_rs/test/traits/generic_traits.rs;l=38 + std::int32_t y; + }; + + private: + static void __crubit_field_offset_assertions(); +}; + +// Generated from: +// cc_bindings_from_rs/test/traits/generic_traits.rs;l=9 +struct CRUBIT_INTERNAL_RUST_TYPE( + ":: generic_traits_golden :: StructGeneric") alignas(4) + [[clang::trivial_abi]] StructGeneric final { + public: + // `generic_traits_golden::StructGeneric` doesn't implement the `Default` + // trait + StructGeneric() = delete; + + // No custom `Drop` impl and no custom "drop glue" required + ~StructGeneric() = default; + StructGeneric(StructGeneric&&) = default; + ::generic_traits::StructGeneric& operator=(StructGeneric&&) = default; + + // `generic_traits_golden::StructGeneric` doesn't implement the `Clone` trait + StructGeneric(const StructGeneric&) = delete; + StructGeneric& operator=(const StructGeneric&) = delete; + StructGeneric(::crubit::UnsafeRelocateTag, StructGeneric&& value) { + memcpy(this, &value, sizeof(value)); + } + + // Generated from: + // cc_bindings_from_rs/test/traits/generic_traits.rs;l=14 + static ::generic_traits::StructGeneric new_(std::int32_t x); + + union { + // Generated from: + // cc_bindings_from_rs/test/traits/generic_traits.rs;l=10 + std::int32_t x; + }; + + private: + static void __crubit_field_offset_assertions(); +}; + +// Error generating bindings for `generic_traits_golden::TraitWithConst` defined +// at +// cc_bindings_from_rs/test/traits/generic_traits.rs;l=49: +// Trait is not yet supported + +// Generated from: +// cc_bindings_from_rs/test/traits/generic_traits.rs;l=5 +template +struct CRUBIT_INTERNAL_RUST_TYPE(":: generic_traits_golden :: TraitWithGeneric") + TraitWithGeneric { + template + using impl = rs_std::impl>; +}; + +// Generated from: +// cc_bindings_from_rs/test/traits/generic_traits.rs;l=26 +template +struct CRUBIT_INTERNAL_RUST_TYPE( + ":: generic_traits_golden :: TraitWithTwoGenerics") TraitWithTwoGenerics { + template + using impl = rs_std::impl>; +}; + +static_assert( + sizeof(AnotherStruct) == 4, + "Verify that ADT layout didn't change since this header got generated"); +static_assert( + alignof(AnotherStruct) == 4, + "Verify that ADT layout didn't change since this header got generated"); +static_assert(std::is_trivially_destructible_v); +static_assert( + std::is_trivially_move_constructible_v<::generic_traits::AnotherStruct>); +static_assert( + std::is_trivially_move_assignable_v<::generic_traits::AnotherStruct>); +inline void AnotherStruct::__crubit_field_offset_assertions() { + static_assert(0 == offsetof(AnotherStruct, y)); +} +static_assert( + sizeof(StructGeneric) == 4, + "Verify that ADT layout didn't change since this header got generated"); +static_assert( + alignof(StructGeneric) == 4, + "Verify that ADT layout didn't change since this header got generated"); +static_assert(std::is_trivially_destructible_v); +static_assert( + std::is_trivially_move_constructible_v<::generic_traits::StructGeneric>); +static_assert( + std::is_trivially_move_assignable_v<::generic_traits::StructGeneric>); +namespace __crubit_internal { +extern "C" void __crubit_thunk_new(std::int32_t, + ::generic_traits::StructGeneric* __ret_ptr); +} +inline ::generic_traits::StructGeneric StructGeneric::new_(std::int32_t x) { + crubit::Slot<::generic_traits::StructGeneric> __return_value_ret_val_holder; + auto* __return_value_storage = __return_value_ret_val_holder.Get(); + __crubit_internal::__crubit_thunk_new(x, __return_value_storage); + return std::move(__return_value_ret_val_holder).AssumeInitAndTakeValue(); +} +inline void StructGeneric::__crubit_field_offset_assertions() { + static_assert(0 == offsetof(StructGeneric, x)); +} +} // namespace generic_traits + +template <> +struct rs_std::impl<::generic_traits::StructGeneric, + ::generic_traits::TraitWithGeneric> { + static constexpr bool kIsImplemented = true; + + // Generated from: + // cc_bindings_from_rs/test/traits/generic_traits.rs;l=21 + static std::int32_t foo(::generic_traits::StructGeneric const& self, + std::int32_t t); +}; + +template <> +struct rs_std::impl< + ::generic_traits::StructGeneric, + ::generic_traits::TraitWithTwoGenerics> { + static constexpr bool kIsImplemented = true; + + // Generated from: + // cc_bindings_from_rs/test/traits/generic_traits.rs;l=32 + static std::int32_t bar(::generic_traits::StructGeneric const& self, + std::int32_t t, std::int32_t u); +}; + +// Error generating bindings for `>` defined at +// cc_bindings_from_rs/test/traits/generic_traits.rs;l=42: +// The following Rust type is not supported yet: U + +namespace generic_traits { +namespace __crubit_internal { +extern "C" std::int32_t __crubit_thunk_TraitWithGeneric_ufoo( + ::generic_traits::StructGeneric const&, std::int32_t); +} +} // namespace generic_traits +inline std::int32_t +rs_std::impl<::generic_traits::StructGeneric, + ::generic_traits::TraitWithGeneric>:: + foo(::generic_traits::StructGeneric const& self, std::int32_t t) { + return generic_traits::__crubit_internal:: + __crubit_thunk_TraitWithGeneric_ufoo(self, t); +} + +namespace generic_traits { +namespace __crubit_internal { +extern "C" std::int32_t __crubit_thunk_TraitWithTwoGenerics_ubar( + ::generic_traits::StructGeneric const&, std::int32_t, std::int32_t); +} +} // namespace generic_traits +inline std::int32_t rs_std::impl< + ::generic_traits::StructGeneric, + ::generic_traits::TraitWithTwoGenerics>:: + bar(::generic_traits::StructGeneric const& self, std::int32_t t, + std::int32_t u) { + return generic_traits::__crubit_internal:: + __crubit_thunk_TraitWithTwoGenerics_ubar(self, t, u); +} + +#pragma clang diagnostic pop +#endif // THIRD_PARTY_CRUBIT_CC_BINDINGS_FROM_RS_TEST_TRAITS_GENERIC_TRAITS_GOLDEN diff --git a/cc_bindings_from_rs/test/traits/generic_traits_cc_api_impl.rs b/cc_bindings_from_rs/test/traits/generic_traits_cc_api_impl.rs new file mode 100644 index 000000000..9d3fd5d26 --- /dev/null +++ b/cc_bindings_from_rs/test/traits/generic_traits_cc_api_impl.rs @@ -0,0 +1,47 @@ +// 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 C++ bindings for the following Rust crate: +// generic_traits_golden +// Features: assume_lifetimes, assume_this_lifetimes, callables, check_default_initialized, experimental, fmt, supported, unsafe_view, wrapper + +#![allow(unused_unsafe, deprecated, non_snake_case, unreachable_code)] +#![allow(improper_ctypes_definitions)] +#![deny(warnings)] + +const _: () = assert!(::std::mem::size_of::<::generic_traits_golden::AnotherStruct>() == 4); +const _: () = assert!(::std::mem::align_of::<::generic_traits_golden::AnotherStruct>() == 4); +const _: () = assert!(::core::mem::offset_of!(::generic_traits_golden::AnotherStruct, y) == 0); +const _: () = assert!(::std::mem::size_of::<::generic_traits_golden::StructGeneric>() == 4); +const _: () = assert!(::std::mem::align_of::<::generic_traits_golden::StructGeneric>() == 4); +#[unsafe(no_mangle)] +unsafe extern "C" fn __crubit_thunk_new(x: i32, __ret_ptr: *mut core::ffi::c_void) -> () { + unsafe { + let __rs_return_value = ::generic_traits_golden::StructGeneric::new(x); + (__ret_ptr as *mut ::generic_traits_golden::StructGeneric).write(__rs_return_value); + } +} +const _: () = assert!(::core::mem::offset_of!(::generic_traits_golden::StructGeneric, x) == 0); +#[unsafe(no_mangle)] +unsafe extern "C" fn __crubit_thunk_TraitWithGeneric_ufoo( + __self: &'static ::generic_traits_golden::StructGeneric, + t: i32, +) -> i32 { + unsafe { + <::generic_traits_golden::StructGeneric as::generic_traits_golden::TraitWithGeneric>::foo(__self,t) + } +} +#[unsafe(no_mangle)] +unsafe extern "C" fn __crubit_thunk_TraitWithTwoGenerics_ubar( + __self: &'static ::generic_traits_golden::StructGeneric, + t: i32, + u: i32, +) -> i32 { + unsafe { + <::generic_traits_golden::StructGeneric as ::generic_traits_golden::TraitWithTwoGenerics< + i32, + i32, + >>::bar(__self, t, u) + } +} diff --git a/cc_bindings_from_rs/test/traits/generic_traits_test.cc b/cc_bindings_from_rs/test/traits/generic_traits_test.cc new file mode 100644 index 000000000..954f48538 --- /dev/null +++ b/cc_bindings_from_rs/test/traits/generic_traits_test.cc @@ -0,0 +1,42 @@ +// 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 + +#include "cc_bindings_from_rs/test/traits/generic_traits.h" + +#include "gtest/gtest.h" + +namespace generic_traits { + +template + requires(rs_std::where_v>) +T generic_do_something(Self const& self, T t, U u) { + return TraitWithTwoGenerics::template impl::bar(self, t, u); +} + +TEST(GenericTraitTest, FullySpecifiedTraitImplBound) { + StructGeneric s = StructGeneric::new_(10); + EXPECT_TRUE(TraitWithGeneric::impl::kIsImplemented); + EXPECT_EQ(TraitWithGeneric::impl::foo(s, 5), 15); +} + +TEST(GenericTraitTest, FullySpecifiedMultipleGenericsTraitImplBound) { + StructGeneric s = StructGeneric::new_(123); + EXPECT_TRUE( + (TraitWithTwoGenerics::impl::kIsImplemented)); + EXPECT_EQ((TraitWithTwoGenerics::impl::bar( + s, 5, 7)), + 135); + EXPECT_EQ(generic_do_something(s, 5, 7), 135); +} + +TEST(GenericTraitTest, AnotherStructDoesNotImplementTrait) { + static_assert( + !TraitWithTwoGenerics::impl::kIsImplemented); +} + +// TraitWithConstant doesn't receive bindings. + +} // namespace generic_traits diff --git a/cc_bindings_from_rs/test/traits/traits.rs b/cc_bindings_from_rs/test/traits/traits.rs index 1677eb0ec..0d6dd56e4 100644 --- a/cc_bindings_from_rs/test/traits/traits.rs +++ b/cc_bindings_from_rs/test/traits/traits.rs @@ -36,10 +36,6 @@ trait DoesNotBind { fn do_something(&self) -> i32; } -pub trait GenericTrait { - fn generic_do_something(&self) -> T; -} - pub trait LifetimeTrait<'a> { fn trait_do_something(&'a self) -> &'a i32; @@ -91,12 +87,6 @@ impl DoesNotBind for MyStruct { } } -impl GenericTrait for MyStruct { - fn generic_do_something(&self) -> i32 { - self.x - } -} - pub struct LifetimeStruct<'a> { x: &'a i32, } diff --git a/cc_bindings_from_rs/test/traits/traits_cc_api.h b/cc_bindings_from_rs/test/traits/traits_cc_api.h index 5011f3003..53aa7b02b 100644 --- a/cc_bindings_from_rs/test/traits/traits_cc_api.h +++ b/cc_bindings_from_rs/test/traits/traits_cc_api.h @@ -31,7 +31,7 @@ namespace traits { // Generated from: -// cc_bindings_from_rs/test/traits/traits.rs;l=152 +// cc_bindings_from_rs/test/traits/traits.rs;l=142 struct CRUBIT_INTERNAL_RUST_TYPE( ":: traits_golden :: AssociatedTypeStruct") alignas(8) [[clang::trivial_abi]] AssociatedTypeStruct final { @@ -63,7 +63,7 @@ struct CRUBIT_INTERNAL_RUST_TYPE( std::array a; union { // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=153 + // cc_bindings_from_rs/test/traits/traits.rs;l=143 std::int32_t b; }; unsigned char __padding0[4]; @@ -73,7 +73,7 @@ struct CRUBIT_INTERNAL_RUST_TYPE( }; // Generated from: -// cc_bindings_from_rs/test/traits/traits.rs;l=141 +// cc_bindings_from_rs/test/traits/traits.rs;l=131 struct CRUBIT_INTERNAL_RUST_TYPE(":: traits_golden :: AssociatedTypeTrait") AssociatedTypeTrait { template @@ -123,12 +123,8 @@ struct CRUBIT_INTERNAL_RUST_TYPE(":: traits_golden :: Foo") alignas(4) static void __crubit_field_offset_assertions(); }; -// Error generating bindings for `traits_golden::GenericTrait` defined at -// cc_bindings_from_rs/test/traits/traits.rs;l=39: -// Trait is not yet supported - // Generated from: -// cc_bindings_from_rs/test/traits/traits.rs;l=100 +// cc_bindings_from_rs/test/traits/traits.rs;l=90 struct CRUBIT_INTERNAL_RUST_TYPE(":: traits_golden :: LifetimeStruct") alignas( 8) [[clang::trivial_abi]] LifetimeStruct final { public: @@ -150,7 +146,7 @@ struct CRUBIT_INTERNAL_RUST_TYPE(":: traits_golden :: LifetimeStruct") alignas( private: union { // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=101 + // cc_bindings_from_rs/test/traits/traits.rs;l=91 std::int32_t const* crubit_nonnull x; }; @@ -159,7 +155,7 @@ struct CRUBIT_INTERNAL_RUST_TYPE(":: traits_golden :: LifetimeStruct") alignas( }; // Generated from: -// cc_bindings_from_rs/test/traits/traits.rs;l=43 +// cc_bindings_from_rs/test/traits/traits.rs;l=39 struct CRUBIT_INTERNAL_RUST_TYPE(":: traits_golden :: LifetimeTrait") LifetimeTrait { template @@ -167,7 +163,7 @@ struct CRUBIT_INTERNAL_RUST_TYPE(":: traits_golden :: LifetimeTrait") }; // Generated from: -// cc_bindings_from_rs/test/traits/traits.rs;l=50 +// cc_bindings_from_rs/test/traits/traits.rs;l=46 struct CRUBIT_INTERNAL_RUST_TYPE(":: traits_golden :: MyStruct") alignas(4) [[clang::trivial_abi]] MyStruct final { public: @@ -188,13 +184,13 @@ struct CRUBIT_INTERNAL_RUST_TYPE(":: traits_golden :: MyStruct") alignas(4) } // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=55 + // cc_bindings_from_rs/test/traits/traits.rs;l=51 static ::traits::MyStruct new_(std::int32_t x); private: union { // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=51 + // cc_bindings_from_rs/test/traits/traits.rs;l=47 std::int32_t x; }; @@ -203,7 +199,7 @@ struct CRUBIT_INTERNAL_RUST_TYPE(":: traits_golden :: MyStruct") alignas(4) }; // Generated from: -// cc_bindings_from_rs/test/traits/traits.rs;l=115 +// cc_bindings_from_rs/test/traits/traits.rs;l=105 struct CRUBIT_INTERNAL_RUST_TYPE(":: traits_golden :: MyStruct2") alignas(4) [[clang::trivial_abi]] MyStruct2 final { public: @@ -226,7 +222,7 @@ struct CRUBIT_INTERNAL_RUST_TYPE(":: traits_golden :: MyStruct2") alignas(4) private: union { // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=116 + // cc_bindings_from_rs/test/traits/traits.rs;l=106 std::int32_t y; }; @@ -244,7 +240,7 @@ struct CRUBIT_INTERNAL_RUST_TYPE(":: traits_golden :: MyTrait") MyTrait { }; // Generated from: -// cc_bindings_from_rs/test/traits/traits.rs;l=177 +// cc_bindings_from_rs/test/traits/traits.rs;l=167 struct CRUBIT_INTERNAL_RUST_TYPE( ":: traits_golden :: StructWithAssociatedConst") alignas(4) [[clang::trivial_abi]] StructWithAssociatedConst final { @@ -269,7 +265,7 @@ struct CRUBIT_INTERNAL_RUST_TYPE( } union { // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=178 + // cc_bindings_from_rs/test/traits/traits.rs;l=168 std::int32_t x; }; @@ -278,7 +274,7 @@ struct CRUBIT_INTERNAL_RUST_TYPE( }; // Generated from: -// cc_bindings_from_rs/test/traits/traits.rs;l=170 +// cc_bindings_from_rs/test/traits/traits.rs;l=160 struct CRUBIT_INTERNAL_RUST_TYPE(":: traits_golden :: TraitWithAssociatedConst") TraitWithAssociatedConst { template @@ -458,24 +454,24 @@ struct rs_std::impl<::traits::MyStruct, ::traits::MyTrait> { static constexpr bool kIsImplemented = true; // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=61 + // cc_bindings_from_rs/test/traits/traits.rs;l=57 static std::int32_t do_something(::traits::MyStruct const& self); // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=65 + // cc_bindings_from_rs/test/traits/traits.rs;l=61 static std::int32_t consume_self(::traits::MyStruct self); // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=69 + // cc_bindings_from_rs/test/traits/traits.rs;l=65 static ::traits::MyStruct const& $(__anon1) return_self(::traits::MyStruct const& self); // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=73 + // cc_bindings_from_rs/test/traits/traits.rs;l=69 static std::int32_t no_self(); // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=77 + // cc_bindings_from_rs/test/traits/traits.rs;l=73 static std::tuple take_and_return_other_types( ::traits::MyStruct const& self, ::traits::Foo x); }; @@ -485,24 +481,24 @@ struct rs_std::impl<::traits::MyStruct2, ::traits::MyTrait> { static constexpr bool kIsImplemented = true; // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=120 + // cc_bindings_from_rs/test/traits/traits.rs;l=110 static std::int32_t do_something(::traits::MyStruct2 const& self); // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=124 + // cc_bindings_from_rs/test/traits/traits.rs;l=114 static std::int32_t consume_self(::traits::MyStruct2 self); // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=128 + // cc_bindings_from_rs/test/traits/traits.rs;l=118 static ::traits::MyStruct2 const& $(__anon1) return_self(::traits::MyStruct2 const& self); // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=132 + // cc_bindings_from_rs/test/traits/traits.rs;l=122 static std::int32_t no_self(); // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=136 + // cc_bindings_from_rs/test/traits/traits.rs;l=126 static std::tuple take_and_return_other_types( ::traits::MyStruct2 const& self, ::traits::Foo x); }; @@ -512,7 +508,7 @@ struct rs_std::impl<::traits::MyStruct, ::traits::DifferentTraitSameName> { static constexpr bool kIsImplemented = true; // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=83 + // cc_bindings_from_rs/test/traits/traits.rs;l=79 static std::int32_t do_something(::traits::MyStruct const& self); }; @@ -521,12 +517,12 @@ struct rs_std::impl<::traits::LifetimeStruct, ::traits::LifetimeTrait> { static constexpr bool kIsImplemented = true; // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=105 + // cc_bindings_from_rs/test/traits/traits.rs;l=95 static std::int32_t const& $a trait_do_something(::traits::LifetimeStruct const& self); // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=109 + // cc_bindings_from_rs/test/traits/traits.rs;l=99 static std::int32_t const& $(__anon1) function_do_something(::traits::LifetimeStruct const& self); }; @@ -540,19 +536,19 @@ struct rs_std::impl<::traits::AssociatedTypeStruct, "AssociatedTypeTrait>::MyAssocType") = std::int32_t; // Generated from: - // cc_bindings_from_rs/test/traits/traits.rs;l=159 + // cc_bindings_from_rs/test/traits/traits.rs;l=149 static std::int32_t get_my_assoc_type( ::traits::AssociatedTypeStruct const& self); // Error generating bindings for `::UnsupportedAssocType` defined at - // cc_bindings_from_rs/test/traits/traits.rs;l=164: + // cc_bindings_from_rs/test/traits/traits.rs;l=154: // Type `std::string::String` comes from the `alloc` crate, but no // `--crate-header` was specified for this crate // Error generating bindings for `::get_unsupported_assoc_type` defined at - // cc_bindings_from_rs/test/traits/traits.rs;l=165: + // cc_bindings_from_rs/test/traits/traits.rs;l=155: // Error formatting function return type `std::string::String`: Type // `std::string::String` comes from the `alloc` crate, but no `--crate-header` // was specified for this crate @@ -566,7 +562,7 @@ struct rs_std::impl<::traits::StructWithAssociatedConst, // Error generating bindings for `::CONST_STRUCT` defined at - // cc_bindings_from_rs/test/traits/traits.rs;l=184: + // cc_bindings_from_rs/test/traits/traits.rs;l=174: // Unsupported constant type: traits_golden::StructWithAssociatedConst }; diff --git a/cc_bindings_from_rs/test/traits/traits_test.cc b/cc_bindings_from_rs/test/traits/traits_test.cc index e6c147943..a51bb4741 100644 --- a/cc_bindings_from_rs/test/traits/traits_test.cc +++ b/cc_bindings_from_rs/test/traits/traits_test.cc @@ -3,7 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "cc_bindings_from_rs/test/traits/traits.h" -#include "gmock/gmock.h" #include "gtest/gtest.h" template