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
61 changes: 51 additions & 10 deletions cc_bindings_from_rs/generate_bindings/generate_function_thunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -724,23 +724,60 @@ pub struct TraitThunks<'tcx> {
pub rs_thunk_impls: RsSnippet,
}

/// Traits that Crubit supports generating bindings for.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SupportedTrait<'tcx> {
Into(Ty<'tcx>),
From(Ty<'tcx>),
ToString,
Drop,
IntoIterator,
Default,
Clone,
}

impl<'tcx> SupportedTrait<'tcx> {
pub fn trait_id(&self, tcx: TyCtxt<'_>) -> DefId {
let trait_id = match self {
SupportedTrait::Into(_) => tcx.get_diagnostic_item(sym::Into),
SupportedTrait::From(_) => tcx.get_diagnostic_item(sym::From),
SupportedTrait::ToString => tcx.get_diagnostic_item(Symbol::intern("ToString")),
SupportedTrait::Drop => tcx.lang_items().drop_trait(),
SupportedTrait::IntoIterator => tcx.get_diagnostic_item(sym::IntoIterator),
SupportedTrait::Default => tcx.get_diagnostic_item(sym::Default),
SupportedTrait::Clone => tcx.lang_items().clone_trait(),
};
trait_id.expect("trait id not found, this is a bug")
}

pub fn type_args(&self) -> &[Ty<'tcx>] {
match self {
SupportedTrait::Into(ty) => std::slice::from_ref(ty),
SupportedTrait::From(ty) => std::slice::from_ref(ty),
SupportedTrait::ToString => &[],
SupportedTrait::Drop => &[],
SupportedTrait::IntoIterator => &[],
SupportedTrait::Default => &[],
SupportedTrait::Clone => &[],
}
}
}

#[allow(clippy::too_many_arguments)]
pub fn generate_trait_thunks<'tcx>(
db: &BindingsGenerator<'tcx>,
trait_id: DefId,
// We do not support other generic args, yet.
type_args: &[Ty<'tcx>],
// Accepting SupportedTrait allows us to control the set of traits that can be used.
supported_trait: SupportedTrait<'tcx>,
self_ty: Ty<'tcx>,
def_id: Option<DefId>,
rs_fully_qualified_name: TokenStream,
is_constructor: bool,
within_template: bool,
) -> Result<TraitThunks<'tcx>> {
let tcx = db.tcx();
assert!(tcx.is_trait(trait_id));
let trait_id = supported_trait.trait_id(tcx);

let is_drop_trait = Some(trait_id) == tcx.lang_items().drop_trait();
if is_drop_trait {
if let SupportedTrait::Drop = supported_trait {
// To support "drop glue" we don't require that `self_ty` directly implements
// the `Drop` trait. Instead we require the caller to check
// `needs_drop`.
Expand All @@ -752,7 +789,7 @@ pub fn generate_trait_thunks<'tcx>(
tcx,
self_ty,
trait_id,
type_args.iter().copied().map(ty::GenericArg::from),
supported_trait.type_args().iter().copied().map(ty::GenericArg::from),
) {
let display_name = def_id
.and_then(|id| db.symbol_canonical_name(id))
Expand Down Expand Up @@ -780,7 +817,10 @@ pub fn generate_trait_thunks<'tcx>(
// We don't support trait methods that use `Box<Self>` or `Pin<Self>` yet.
self_ty.pinned_ty().is_none() && self_ty.boxed_ty().is_none()
}
let substs = tcx.mk_args_trait(self_ty, type_args.iter().copied().map(ty::GenericArg::from));
let substs = tcx.mk_args_trait(
self_ty,
supported_trait.type_args().iter().copied().map(ty::GenericArg::from),
);

let mut method_name_to_cc_thunk_name = HashMap::new();
let mut cc_thunk_decls = CcSnippet::default();
Expand Down Expand Up @@ -837,7 +877,7 @@ pub fn generate_trait_thunks<'tcx>(
method_name_to_cc_thunk_name.insert(method.name(), thunk_name_cc_ident);

let struct_name = &rs_fully_qualified_name;
rs_thunk_impls += if is_drop_trait {
rs_thunk_impls += if let SupportedTrait::Drop = supported_trait {
// Manually formatting (instead of depending on `generate_thunk_impl`)
// to avoid https://doc.rust-lang.org/error_codes/E0040.html
let thunk_name = make_rs_ident(&thunk_name);
Expand All @@ -853,7 +893,8 @@ pub fn generate_trait_thunks<'tcx>(
.ok_or_else(|| anyhow!("Failed to get canonical name for {trait_id:?}"))?
.format_for_rs();
let method_name = make_rs_ident(method.name().as_str());
let args = type_args
let args = supported_trait
.type_args()
.iter()
.map(|ty| {
let static_ty = replace_all_regions_with_static(tcx, *ty);
Expand Down
56 changes: 15 additions & 41 deletions cc_bindings_from_rs/generate_bindings/generate_struct_and_union.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::{
does_type_implement_trait, generate_const, generate_deprecated_tag, generate_must_use_tag,
generate_trait_thunks, generate_unsupported_def, get_layout, get_scalar_int_type,
get_tag_size_with_padding, is_bridged_type, is_copy, BridgedBuiltin, RsSnippet, SortedByDef,
TraitThunks,
SupportedTrait, TraitThunks,
};

use arc_anyhow::{Context, Result};
Expand Down Expand Up @@ -573,8 +573,7 @@ fn generate_into_impls<'tcx>(
rs_thunk_impls: rs_details,
} = generate_trait_thunks(
db,
into_trait,
&[middle_ty],
SupportedTrait::Into(middle_ty),
core.self_ty,
core.def_id,
core.rs_fully_qualified_name.clone(),
Expand Down Expand Up @@ -771,8 +770,7 @@ fn generate_constructor_impls<'tcx>(
rs_thunk_impls: rs_details,
} = generate_trait_thunks(
db,
from_trait,
&[src_ty],
SupportedTrait::From(src_ty),
core.self_ty,
core.def_id,
core.rs_fully_qualified_name.clone(),
Expand Down Expand Up @@ -1343,15 +1341,10 @@ fn generate_display_impl<'tcx>(
return err_snippets(anyhow!("`core` crate does not support `std::fmt::Display` because it requires `alloc::string::String` which is not available in `core`"));
}

let Some(to_string_trait_id) = tcx.get_diagnostic_item(Symbol::intern("ToString")) else {
return err_snippets(anyhow!("Internal Crubit Error: `ToString` trait not found."));
};

let TraitThunks { method_name_to_cc_thunk_name, cc_thunk_decls, rs_thunk_impls: rs_details } =
match generate_trait_thunks(
db,
to_string_trait_id,
&[],
SupportedTrait::ToString,
core.self_ty,
core.def_id,
core.rs_fully_qualified_name.clone(),
Expand Down Expand Up @@ -1423,16 +1416,13 @@ pub fn generate_adt<'tcx>(
let default_ctor_snippets = db.generate_default_ctor(core.clone()).unwrap_or_else(|err| err);

let destructor_snippets = if adt_core_bindings_needs_drop(&core, tcx) {
let drop_trait_id =
tcx.lang_items().drop_trait().expect("`Drop` trait should be present if `needs_drop");
let TraitThunks {
method_name_to_cc_thunk_name,
mut cc_thunk_decls,
rs_thunk_impls: rs_details,
} = generate_trait_thunks(
db,
drop_trait_id,
&[],
SupportedTrait::Drop,
core.self_ty,
core.def_id,
core.rs_fully_qualified_name.clone(),
Expand Down Expand Up @@ -2996,13 +2986,9 @@ enum PassingMode {
MutRef,
}

fn get_into_iter_ty<'tcx>(
tcx: TyCtxt<'tcx>,
self_ty: Ty<'tcx>,
into_iterator_trait_id: DefId,
) -> Result<Ty<'tcx>> {
fn get_into_iter_ty<'tcx>(tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Result<Ty<'tcx>> {
let into_iter_assoc_item = tcx
.associated_items(into_iterator_trait_id)
.associated_items(SupportedTrait::IntoIterator.trait_id(tcx))
.in_definition_order()
.find(|item| {
item.name() == rustc_span::symbol::Symbol::intern("IntoIter")
Expand All @@ -3026,13 +3012,9 @@ fn get_into_iter_ty<'tcx>(
.map_err(|_| anyhow!("Failed to normalize `<{} as IntoIterator>::IntoIter`", self_ty))
}

fn get_into_iter_item_ty<'tcx>(
tcx: TyCtxt<'tcx>,
self_ty: Ty<'tcx>,
into_iterator_trait_id: DefId,
) -> Result<Ty<'tcx>> {
fn get_into_iter_item_ty<'tcx>(tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Result<Ty<'tcx>> {
let item_assoc_item = tcx
.associated_items(into_iterator_trait_id)
.associated_items(SupportedTrait::IntoIterator.trait_id(tcx))
.in_definition_order()
.find(|item| {
item.name() == Symbol::intern("Item") && matches!(item.kind, ty::AssocKind::Type { .. })
Expand All @@ -3057,7 +3039,6 @@ fn get_into_iter_item_ty<'tcx>(
fn generate_begin_and_end_for_type<'tcx>(
db: &BindingsGenerator<'tcx>,
core: &AdtCoreBindings<'tcx>,
into_iterator_trait_id: DefId,
passing_mode: PassingMode,
) -> Result<Option<ApiSnippets<'tcx>>> {
let tcx = db.tcx();
Expand Down Expand Up @@ -3097,13 +3078,13 @@ fn generate_begin_and_end_for_type<'tcx>(
return Ok(Some(ApiSnippets { main_api, ..Default::default() }));
}

if !does_type_implement_trait(tcx, check_ty, into_iterator_trait_id, []) {
if !does_type_implement_trait(tcx, check_ty, SupportedTrait::IntoIterator.trait_id(tcx), []) {
return Ok(None);
}

let into_iter_ty = get_into_iter_ty(tcx, check_ty, into_iterator_trait_id)?;
let into_iter_ty = get_into_iter_ty(tcx, check_ty)?;

let item_ty = get_into_iter_item_ty(tcx, check_ty, into_iterator_trait_id)?;
let item_ty = get_into_iter_item_ty(tcx, check_ty)?;

let _ = db
.format_ty_for_cc(item_ty, TypeLocation::Other)
Expand All @@ -3119,8 +3100,7 @@ fn generate_begin_and_end_for_type<'tcx>(
let TraitThunks { method_name_to_cc_thunk_name, cc_thunk_decls, rs_thunk_impls } =
generate_trait_thunks(
db,
into_iterator_trait_id,
&[],
SupportedTrait::IntoIterator,
check_ty,
core.def_id,
rs_fully_qualified_name,
Expand All @@ -3133,7 +3113,7 @@ fn generate_begin_and_end_for_type<'tcx>(
.expect("IntoIterator trait missing into_iter method");

let into_iter_fn_assoc_item = tcx
.associated_items(into_iterator_trait_id)
.associated_items(SupportedTrait::IntoIterator.trait_id(tcx))
.in_definition_order()
.find(|item| item.name() == sym::into_iter && matches!(item.kind, ty::AssocKind::Fn { .. }))
.expect("IntoIterator should have into_iter method");
Expand Down Expand Up @@ -3278,8 +3258,6 @@ fn generate_into_iterator_impls<'tcx>(
core: &AdtCoreBindings<'tcx>,
member_function_names: &mut HashSet<String>,
) -> Result<ApiSnippets<'tcx>> {
let tcx = db.tcx();

if member_function_names.contains("begin")
|| member_function_names.contains("end")
|| member_function_names.contains("into_iter")
Expand All @@ -3296,13 +3274,9 @@ fn generate_into_iterator_impls<'tcx>(
bail!("{} has a field named `begin`, `end`, or `into_iter`, which prevents binding methods for IntoIterator.", core.self_ty);
}

let into_iterator_trait_id = tcx
.get_diagnostic_item(sym::IntoIterator)
.expect("Could not find IntoIterator trait. Please file a crubit bug.");

Ok([PassingMode::Value, PassingMode::SharedRef, PassingMode::MutRef]
.into_iter()
.map(|mode| generate_begin_and_end_for_type(db, core, into_iterator_trait_id, mode))
.map(|mode| generate_begin_and_end_for_type(db, core, mode))
.filter_map(|result| {
result.unwrap_or_else(|err| {
core.def_id.map(|def_id| generate_unsupported_def(db, def_id, err).into_main_api())
Expand Down
15 changes: 3 additions & 12 deletions cc_bindings_from_rs/generate_bindings/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use crate::format_type::{
BridgedType, BridgedTypeConversionInfo,
};
use crate::generate_function::{generate_function, must_use_attr_of};
use crate::generate_function_thunk::{generate_trait_thunks, TraitThunks};
use crate::generate_function_thunk::{generate_trait_thunks, SupportedTrait, 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, into_trait_impls_by_destination, scalar_value_to_string,
Expand Down Expand Up @@ -1340,17 +1340,13 @@ fn generate_default_ctor<'tcx>(
core: Rc<AdtCoreBindings<'tcx>>,
) -> Result<ApiSnippets<'tcx>> {
let tcx = db.tcx();
let trait_id = tcx
.get_diagnostic_item(sym::Default)
.ok_or(anyhow!("Couldn't find `core::default::Default`"))?;
let TraitThunks {
method_name_to_cc_thunk_name,
cc_thunk_decls,
rs_thunk_impls: rs_details,
} = generate_trait_thunks(
db,
trait_id,
&[],
SupportedTrait::Default,
core.self_ty,
core.def_id,
core.rs_fully_qualified_name.clone(),
Expand Down Expand Up @@ -1459,18 +1455,13 @@ fn generate_copy_ctor_and_assignment_operator<'tcx>(
Ok(ApiSnippets { main_api, cc_details, rs_details: RsSnippet::default() })
}
Some(CopyCtorStyle::Clone) => {
let trait_id = tcx
.lang_items()
.clone_trait()
.ok_or_else(|| anyhow!("Can't find the `Clone` trait"))?;
let TraitThunks {
method_name_to_cc_thunk_name,
cc_thunk_decls,
rs_thunk_impls: rs_details,
} = generate_trait_thunks(
db,
trait_id,
&[],
SupportedTrait::Clone,
core.self_ty,
core.def_id,
core.rs_fully_qualified_name.clone(),
Expand Down