Skip to content

Commit

Permalink
Support dependent qualified names.
Browse files Browse the repository at this point in the history
For a situation like this:

    struct InnerType {
        typedef int related_type;
        int foo;
        int foo2;
    };

    template <typename ContainedType> class Container {
    public:
        typedef typename ContainedType::related_type content_ty;
        content_ty contents_;
    };

    typedef Container<InnerType> Concrete;

    struct LaterContainingType {
        Concrete outer_contents;
    };

previously bindgen would have generated an opaque type. It now
correctly recognizes that the contents_ field is of a new
TypeKind::DependentQualifiedName and correctly identifies the
right type to use when instantiating Concrete.

The generated code looks like this:

    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone)]
    pub struct InnerType {
        pub foo: ::std::os::raw::c_int,
        pub foo2: ::std::os::raw::c_int,
    }
    pub type InnerType_related_type = ::std::os::raw::c_int;
    pub trait __bindgen_has_inner_type_related_type {
        type related_type: std::fmt::Debug + Default + Copy + Clone;
    }
    impl __bindgen_has_inner_type_related_type for InnerType {
        type related_type = InnerType_related_type;
    }
    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone)]
    pub struct Container<ContainedType: __bindgen_has_inner_type_related_type> {
        pub contents_: Container_content_ty<ContainedType>,
        pub _phantom_0:
            ::std::marker::PhantomData<::std::cell::UnsafeCell<ContainedType>>,
    }
    pub type Container_content_ty<ContainedType> =
        <ContainedType as __bindgen_has_inner_type_related_type>::related_type;
    pub type Concrete = Container<InnerType>;
    #[repr(C)]
    #[derive(Debug, Default, Copy, Clone)]
    pub struct LaterContainingType {
        pub outer_contents: Concrete,
    }

Note the trait constructed to mark types which have an inner type.
This trait is then used in the Rust definition of Container_content_ty.
Such a trait is emitted only if it's actually used, to avoid too much
verbosity.

This is useful for types which are parameterized with a traits type.
For example Chromium's base::StringPiece which is parameterized by
an STL string type (e.g. std::string) and looks up the correct size
of character to use by using a parameterized type:
   typedef typename STRING_TYPE::value_type value_type;
(Of course, std::string and other string types have other reasons
why they're difficult to work with from Rust, such as self-referential
pointers, but that's another story).

This change assumes all types involved derive the same traits:
in the above example note that __bindgen_has_inner_type_related_type
requires all traits be derived. It would be possible to be more nuanced
here and move those trait bounds to the places where the trait is used
(e.g. pub type Concrete = ... in the above example).
  • Loading branch information
adetaylor committed Apr 23, 2021
1 parent 5761a88 commit 142b91c
Show file tree
Hide file tree
Showing 16 changed files with 559 additions and 51 deletions.
25 changes: 16 additions & 9 deletions src/clang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1282,23 +1282,30 @@ impl Type {
/// typename T::Associated member;
/// };
/// ```
pub fn is_associated_type(&self) -> bool {
///
/// If so, returns the type name, e.g. 'member'
pub fn get_associated_type_name(&self) -> Option<String> {
// This is terrible :(
fn hacky_parse_associated_type<S: AsRef<str>>(spelling: S) -> bool {
fn hacky_parse_associated_type<S: AsRef<str>>(
spelling: S,
) -> Option<String> {
lazy_static! {
static ref ASSOC_TYPE_RE: regex::Regex = regex::Regex::new(
r"typename type\-parameter\-\d+\-\d+::.+"
r"typename type\-parameter\-\d+\-\d+::(.+)"
)
.unwrap();
}
ASSOC_TYPE_RE.is_match(spelling.as_ref())
ASSOC_TYPE_RE
.captures(spelling.as_ref())
.map(|caps| caps.get(1).unwrap().as_str().to_string())
}

self.kind() == CXType_Unexposed &&
(hacky_parse_associated_type(self.spelling()) ||
hacky_parse_associated_type(
self.canonical_type().spelling(),
))
if self.kind() != CXType_Unexposed {
return None;
}
hacky_parse_associated_type(self.spelling()).or_else(|| {
hacky_parse_associated_type(self.canonical_type().spelling())
})
}
}

Expand Down
1 change: 1 addition & 0 deletions src/codegen/impl_debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ impl<'a> ImplDebug<'a> for Item {
TypeKind::ObjCInterface(..) |
TypeKind::ObjCId |
TypeKind::Comp(..) |
TypeKind::DependentQualifiedType(..) |
TypeKind::ObjCSel => debug_print(name, quote! { #name_ident }),

TypeKind::TemplateInstantiation(ref inst) => {
Expand Down
1 change: 1 addition & 0 deletions src/codegen/impl_partialeq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ fn gen_field(
TypeKind::Float(..) |
TypeKind::Enum(..) |
TypeKind::TypeParam |
TypeKind::DependentQualifiedType(..) |
TypeKind::UnresolvedTypeRef(..) |
TypeKind::Reference(..) |
TypeKind::ObjCInterface(..) |
Expand Down
139 changes: 138 additions & 1 deletion src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ struct CodegenResult<'a> {
/// function name to the number of overloads we have already codegen'd for
/// that name. This lets us give each overload a unique suffix.
overload_counters: HashMap<String, u32>,

/// Used to record what traits we have generated for the names
/// of inner types, such that we generate each trait exactly once.
/// (The trait may be implemented by multiple types.)
inner_type_traits_generated: HashSet<Ident>,
}

impl<'a> CodegenResult<'a> {
Expand All @@ -248,6 +253,7 @@ impl<'a> CodegenResult<'a> {
functions_seen: Default::default(),
vars_seen: Default::default(),
overload_counters: Default::default(),
inner_type_traits_generated: Default::default(),
}
}

Expand Down Expand Up @@ -393,6 +399,7 @@ impl AppendImplicitTemplateParams for proc_macro2::TokenStream {
TypeKind::Complex(..) |
TypeKind::Array(..) |
TypeKind::TypeParam |
TypeKind::DependentQualifiedType(..) |
TypeKind::Opaque |
TypeKind::Function(..) |
TypeKind::Enum(..) |
Expand Down Expand Up @@ -758,6 +765,25 @@ impl CodeGenerator for Type {
// it to BindgenContext::compute_allowlisted_and_codegen_items.
return;
}
TypeKind::DependentQualifiedType(_, ref field_name) => {
let shortname = ctx.rust_ident(&field_name);
let traitname = ctx.inner_type_trait_ident(field_name);
if result.inner_type_traits_generated.insert(traitname.clone())
{
let mut type_definition = quote! { #shortname };
let derivable_traits = derives_of_item(item, ctx);
append_associated_type_constraints(
ctx,
&derivable_traits,
&mut type_definition,
);
result.push(quote! {
pub trait #traitname {
type #type_definition;
}
});
}
}
TypeKind::TemplateInstantiation(ref inst) => {
inst.codegen(ctx, result, item)
}
Expand Down Expand Up @@ -1908,10 +1934,48 @@ impl CodeGenerator for CompInfo {

let mut generic_param_names = vec![];

let used_dependent_qualified_types =
item.used_dependent_qualified_types(ctx);
let mut dependent_qualified_types_by_param: HashMap<
crate::ir::context::TypeId,
Vec<&String>,
> = HashMap::default();
for ty_id in used_dependent_qualified_types {
let dependent_qualified_type = ctx.resolve_type(ty_id);
match dependent_qualified_type.kind() {
TypeKind::DependentQualifiedType(
tp_id,
associated_type_name,
) => {
dependent_qualified_types_by_param
.entry(*tp_id)
.or_default()
.push(associated_type_name);
}
_ => panic!(
"unexpected type kind for dependent qualified type type"
),
}
}

let mut where_constraints: std::collections::HashMap<
Ident,
Vec<Ident>,
> = std::collections::HashMap::new();
for (idx, ty) in item.used_template_params(ctx).iter().enumerate() {
let param = ctx.resolve_type(*ty);
let name = param.name().unwrap();
let ident = ctx.rust_ident(name);
if let Some(dependent_qualified_type_field_names) =
dependent_qualified_types_by_param.get(ty)
{
where_constraints.entry(ident.clone()).or_default().extend(
dependent_qualified_type_field_names.into_iter().map(
|field_name| ctx.inner_type_trait_ident(field_name),
),
);
}

generic_param_names.push(ident.clone());

let prefix = ctx.trait_prefix();
Expand Down Expand Up @@ -2013,8 +2077,28 @@ impl CodeGenerator for CompInfo {
}
};

let mut where_constraints_ts = quote! {};
if !where_constraints.is_empty() {
for (i, (k, traits)) in where_constraints.into_iter().enumerate() {
let prefix = if i == 0 {
quote! { where }
} else {
quote! { , }
};
where_constraints_ts.extend(quote! { #prefix #k });
for (j, v) in traits.into_iter().enumerate() {
let sep = if j == 0 {
quote! {:}
} else {
quote! {+}
};
where_constraints_ts.extend(quote! { #sep #v });
}
}
}

tokens.append_all(quote! {
#generics {
#generics #where_constraints_ts {
#( #fields )*
}
});
Expand All @@ -2028,6 +2112,26 @@ impl CodeGenerator for CompInfo {
let child_item = ctx.resolve_item(*ty);
// assert_eq!(child_item.parent_id(), item.id());
child_item.codegen(ctx, result, &());

if let Some(shortname) = child_item.expect_type().name() {
if !ctx.inner_type_used(shortname) {
continue;
}
let child_canonical_name = child_item.canonical_name(ctx);
let child_canonical_name =
ctx.rust_ident(&child_canonical_name);

let traitname = ctx.inner_type_trait_ident(&shortname);
let shortname = ctx.rust_ident(shortname);
let mut template_params = proc_macro2::TokenStream::new();
template_params
.append_implicit_template_params(ctx, child_item);
result.push(quote! {
impl #generics #traitname for #canonical_ident #generics {
type #shortname = #child_canonical_name #template_params;
}
});
}
}

// NOTE: Some unexposed attributes (like alignment attributes) may
Expand Down Expand Up @@ -2255,6 +2359,30 @@ impl CodeGenerator for CompInfo {
}
}

fn append_associated_type_constraints(
ctx: &BindgenContext,
derivable_traits: &DerivableTraits,
ts: &mut proc_macro2::TokenStream,
) {
let trait_bounds: Vec<&'static str> = derivable_traits.clone().into();
let mut done_first = false;
for id in trait_bounds.into_iter() {
let id = match id {
"Debug" => quote! { std::fmt::Debug },
_ => {
let id = ctx.rust_ident(id);
quote! { #id }
}
};
ts.append_all(if done_first {
quote! { + #id }
} else {
quote! { : #id }
});
done_first = true;
}
}

trait MethodCodegen {
fn codegen_method<'a>(
&self,
Expand Down Expand Up @@ -3643,6 +3771,15 @@ impl TryToRustTy for Type {
#ident
})
}
TypeKind::DependentQualifiedType(_, ref field_name) => {
let name = item.canonical_name(ctx);
let ident = ctx.rust_ident(&name);
let dependent_type_ident = ctx.rust_ident(&field_name);
let trait_id = ctx.inner_type_trait_ident(field_name);
Ok(quote! {
< #ident as #trait_id > :: #dependent_type_ident
})
}
TypeKind::ObjCSel => Ok(quote! {
objc::runtime::Sel
}),
Expand Down
3 changes: 3 additions & 0 deletions src/ir/analysis/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ fn consider_edge_default(kind: EdgeKind) -> bool {
EdgeKind::VarType |
EdgeKind::TemplateArgument |
EdgeKind::TemplateDeclaration |
EdgeKind::ContainedDependentQualifiedType |
EdgeKind::DependentQualifiedTypeParam |
EdgeKind::TemplateParameterDefinition => true,

EdgeKind::Constructor |
Expand Down Expand Up @@ -212,6 +214,7 @@ impl<'ctx> CannotDerive<'ctx> {
TypeKind::Float(..) |
TypeKind::Enum(..) |
TypeKind::TypeParam |
TypeKind::DependentQualifiedType(..) |
TypeKind::UnresolvedTypeRef(..) |
TypeKind::Reference(..) |
TypeKind::ObjCInterface(..) |
Expand Down
3 changes: 3 additions & 0 deletions src/ir/analysis/has_float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ impl<'ctx> HasFloat<'ctx> {
EdgeKind::VarType |
EdgeKind::TemplateArgument |
EdgeKind::TemplateDeclaration |
EdgeKind::ContainedDependentQualifiedType |
EdgeKind::DependentQualifiedTypeParam |
EdgeKind::TemplateParameterDefinition => true,

EdgeKind::Constructor |
Expand Down Expand Up @@ -122,6 +124,7 @@ impl<'ctx> MonotoneFramework for HasFloat<'ctx> {
TypeKind::Enum(..) |
TypeKind::Reference(..) |
TypeKind::TypeParam |
TypeKind::DependentQualifiedType(..) |
TypeKind::Opaque |
TypeKind::Pointer(..) |
TypeKind::UnresolvedTypeRef(..) |
Expand Down
3 changes: 3 additions & 0 deletions src/ir/analysis/has_type_param_in_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ impl<'ctx> HasTypeParameterInArray<'ctx> {
EdgeKind::VarType |
EdgeKind::TemplateArgument |
EdgeKind::TemplateDeclaration |
EdgeKind::ContainedDependentQualifiedType |
EdgeKind::DependentQualifiedTypeParam |
EdgeKind::TemplateParameterDefinition => true,

EdgeKind::Constructor |
Expand Down Expand Up @@ -133,6 +135,7 @@ impl<'ctx> MonotoneFramework for HasTypeParameterInArray<'ctx> {
TypeKind::Enum(..) |
TypeKind::Reference(..) |
TypeKind::TypeParam |
TypeKind::DependentQualifiedType(..) |
TypeKind::Opaque |
TypeKind::Pointer(..) |
TypeKind::UnresolvedTypeRef(..) |
Expand Down
2 changes: 1 addition & 1 deletion src/ir/analysis/sizedness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ impl<'ctx> MonotoneFramework for SizednessAnalysis<'ctx> {
self.insert(id, SizednessResult::ZeroSized)
}

TypeKind::TypeParam => {
TypeKind::TypeParam | TypeKind::DependentQualifiedType(..) => {
trace!(
" type params sizedness depends on what they're \
instantiated as"
Expand Down
7 changes: 7 additions & 0 deletions src/ir/analysis/template_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ impl<'ctx> UsedTemplateParameters<'ctx> {
EdgeKind::TemplateDeclaration |
EdgeKind::TemplateParameterDefinition => false,

EdgeKind::DependentQualifiedTypeParam |
EdgeKind::ContainedDependentQualifiedType => true,

// Since we have to be careful about which edges we consider for
// this analysis to be correct, we ignore generic edges. We also
// avoid a `_` wild card to force authors of new edge kinds to
Expand Down Expand Up @@ -534,6 +537,10 @@ impl<'ctx> MonotoneFramework for UsedTemplateParameters<'ctx> {
trace!(" named type, trivially uses itself");
used_by_this_id.insert(id);
}
Some(&TypeKind::DependentQualifiedType(tp_id, ..)) => {
used_by_this_id.insert(id);
used_by_this_id.insert(tp_id.into());
}
// Template instantiations only use their template arguments if the
// template definition uses the corresponding template parameter.
Some(&TypeKind::TemplateInstantiation(ref inst)) => {
Expand Down
Loading

0 comments on commit 142b91c

Please sign in to comment.