summaryrefslogtreecommitdiff
path: root/src/ir/comp.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ir/comp.rs')
-rw-r--r--src/ir/comp.rs718
1 files changed, 718 insertions, 0 deletions
diff --git a/src/ir/comp.rs b/src/ir/comp.rs
new file mode 100644
index 00000000..1e6bf555
--- /dev/null
+++ b/src/ir/comp.rs
@@ -0,0 +1,718 @@
+use super::annotations::Annotations;
+use super::context::BindgenContext;
+use super::context::TypeResolver;
+use super::layout::Layout;
+use super::item::{Item, ItemId};
+use super::ty::{Type, RUST_DERIVE_IN_ARRAY_LIMIT};
+use std::cell::Cell;
+use std::cmp;
+use parse::{ClangItemParser, ParseError};
+use clang;
+
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum CompKind {
+ Struct,
+ Union,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum MethodKind {
+ Static,
+ Normal,
+ Virtual,
+}
+
+/// A struct representing a C++ method, either static, normal, or virtual.
+#[derive(Debug)]
+pub struct Method {
+ kind: MethodKind,
+ /// The signature of the method. Take into account this is not a `Type`
+ /// item, but a `Function` one.
+ ///
+ /// This is tricky and probably this field should be renamed.
+ signature: ItemId,
+ is_const: bool,
+}
+
+impl Method {
+ fn new(kind: MethodKind, signature: ItemId, is_const: bool) -> Self {
+ Method {
+ kind: kind,
+ signature: signature,
+ is_const: is_const,
+ }
+ }
+
+ pub fn kind(&self) -> MethodKind {
+ self.kind
+ }
+
+ pub fn is_virtual(&self) -> bool {
+ self.kind == MethodKind::Virtual
+ }
+
+ pub fn is_static(&self) -> bool {
+ self.kind == MethodKind::Static
+ }
+
+ pub fn signature(&self) -> ItemId {
+ self.signature
+ }
+
+ pub fn is_const(&self) -> bool {
+ self.is_const
+ }
+}
+
+/// A struct representing a C++ field.
+#[derive(Clone, Debug)]
+pub struct Field {
+ /// The name of the field, empty if it's an unnamed bitfield width.
+ name: Option<String>,
+ /// The inner type.
+ ty: ItemId,
+ /// The doc comment on the field if any.
+ comment: Option<String>,
+ /// Annotations for this field, or the default.
+ annotations: Annotations,
+ /// If this field is a bitfield, and how many bits does it contain if it is.
+ bitfield: Option<u32>,
+ /// If the C++ field is marked as `mutable`
+ mutable: bool,
+}
+
+impl Field {
+ pub fn new(name: Option<String>,
+ ty: ItemId,
+ comment: Option<String>,
+ annotations: Option<Annotations>,
+ bitfield: Option<u32>,
+ mutable: bool) -> Field {
+ Field {
+ name: name,
+ ty: ty,
+ comment: comment,
+ annotations: annotations.unwrap_or_default(),
+ bitfield: bitfield,
+ mutable: mutable,
+ }
+ }
+
+ pub fn name(&self) -> Option<&str> {
+ self.name.as_ref().map(|n| &**n)
+ }
+
+ pub fn ty(&self) -> ItemId {
+ self.ty
+ }
+
+ pub fn comment(&self) -> Option<&str> {
+ self.comment.as_ref().map(|c| &**c)
+ }
+
+ pub fn bitfield(&self) -> Option<u32> {
+ self.bitfield
+ }
+
+ pub fn is_mutable(&self) -> bool {
+ self.mutable
+ }
+
+ pub fn annotations(&self) -> &Annotations {
+ &self.annotations
+ }
+}
+
+
+#[derive(Debug)]
+pub struct CompInfo {
+ /// Whether this is a struct or a union.
+ kind: CompKind,
+ /// The members of this struct or union.
+ fields: Vec<Field>,
+ /// The template parameters of this class. These are non-concrete, and
+ /// should always be a Type(TypeKind::Named(name)), but still they need to
+ /// be registered with an unique type id in the context.
+ template_args: Vec<ItemId>,
+ /// The method declarations inside this class, if in C++ mode.
+ methods: Vec<Method>,
+ /// Vector of classes this one inherits from.
+ base_members: Vec<ItemId>,
+ /// The parent reference template if any.
+ ref_template: Option<ItemId>,
+ /// The inner types that were declared inside this class, in something like:
+ ///
+ /// class Foo {
+ /// typedef int FooTy;
+ /// struct Bar {
+ /// int baz;
+ /// };
+ /// }
+ ///
+ /// static Foo::Bar const = {3};
+ inner_types: Vec<ItemId>,
+ /// Set of static constants declared inside this class.
+ inner_vars: Vec<ItemId>,
+ /// Whether this type should generate an vtable (TODO: Should be able to
+ /// look at the virtual methods and ditch this field).
+ has_vtable: bool,
+ /// Whether this type has destructor.
+ has_destructor: bool,
+ /// Whether this type has a base type with more than one member.
+ ///
+ /// TODO: We should be able to compute this.
+ has_nonempty_base: bool,
+ /// If this type has a template parameter which is not a type (e.g.: a size_t)
+ has_non_type_template_params: bool,
+ /// Whether this struct layout is packed.
+ packed: bool,
+ /// Whether this struct is anonymous.
+ is_anonymous: bool,
+ /// Used to know if we've found an opaque attribute that could cause us to
+ /// generate a type with invalid layout. This is explicitly used to avoid us
+ /// generating bad alignments when parsing types like max_align_t.
+ ///
+ /// It's not clear what the behavior should be here, if generating the item
+ /// and pray, or behave as an opaque type.
+ found_unknown_attr: bool,
+ /// Used to detect if we've run in a can_derive_debug cycle while cycling
+ /// around the template arguments.
+ detect_derive_debug_cycle: Cell<bool>,
+ /// Used to detect if we've run in a has_destructor cycle while cycling
+ /// around the template arguments.
+ detect_has_destructor_cycle: Cell<bool>,
+}
+
+impl CompInfo {
+ pub fn new(kind: CompKind) -> Self {
+ CompInfo {
+ kind: kind,
+ fields: vec![],
+ template_args: vec![],
+ methods: vec![],
+ base_members: vec![],
+ ref_template: None,
+ inner_types: vec![],
+ inner_vars: vec![],
+ has_vtable: false,
+ has_destructor: false,
+ has_nonempty_base: false,
+ has_non_type_template_params: false,
+ packed: false,
+ is_anonymous: false,
+ found_unknown_attr: false,
+ detect_derive_debug_cycle: Cell::new(false),
+ detect_has_destructor_cycle: Cell::new(false),
+ }
+ }
+
+ pub fn can_derive_debug(&self, type_resolver: &TypeResolver, layout: Option<Layout>) -> bool {
+ // We can reach here recursively via template parameters of a member,
+ // for example.
+ if self.detect_derive_debug_cycle.get() {
+ warn!("Derive debug cycle detected!");
+ return true;
+ }
+
+ if self.kind == CompKind::Union {
+ let layout = layout.unwrap_or_else(Layout::zero);
+ let size_divisor = cmp::max(1, layout.align);
+ return layout.size / size_divisor <= RUST_DERIVE_IN_ARRAY_LIMIT;
+ }
+
+ self.detect_derive_debug_cycle.set(true);
+
+ let can_derive_debug =
+ self.template_args.iter().all(|ty| {
+ type_resolver.resolve_type(*ty)
+ .can_derive_debug(type_resolver)
+ }) &&
+ self.fields.iter().all(|field| {
+ type_resolver.resolve_type(field.ty)
+ .can_derive_debug(type_resolver)
+ });
+
+ self.detect_derive_debug_cycle.set(false);
+
+ can_derive_debug
+ }
+
+ pub fn is_unsized(&self, type_resolver: &TypeResolver) -> bool {
+ !self.has_vtable(type_resolver) && self.fields.is_empty() &&
+ self.base_members.iter().all(|base| {
+ type_resolver
+ .resolve_type(*base)
+ .canonical_type(type_resolver)
+ .is_unsized(type_resolver)
+ }) &&
+ self.ref_template.map_or(true, |template| {
+ type_resolver.resolve_type(template).is_unsized(type_resolver)
+ })
+ }
+
+ pub fn has_destructor(&self, type_resolver: &TypeResolver) -> bool {
+ if self.detect_has_destructor_cycle.get() {
+ warn!("Cycle detected looking for destructors");
+ // Assume no destructor, since we don't have an explicit one.
+ return false;
+ }
+
+ self.detect_has_destructor_cycle.set(true);
+
+ let has_destructor = self.has_destructor || match self.kind {
+ CompKind::Union => false,
+ CompKind::Struct => {
+ // NB: We can't rely on a type with type parameters
+ // not having destructor.
+ //
+ // This is unfortunate, but...
+ self.ref_template.as_ref().map_or(false, |t| {
+ type_resolver.resolve_type(*t).has_destructor(type_resolver)
+ }) ||
+ self.template_args.iter().any(|t| {
+ type_resolver.resolve_type(*t).has_destructor(type_resolver)
+ }) ||
+ self.base_members.iter().any(|t| {
+ type_resolver.resolve_type(*t).has_destructor(type_resolver)
+ }) ||
+ self.fields.iter().any(|field| {
+ type_resolver.resolve_type(field.ty)
+ .has_destructor(type_resolver)
+ })
+ }
+ };
+
+ self.detect_has_destructor_cycle.set(false);
+
+ has_destructor
+ }
+
+ pub fn can_derive_copy(&self, type_resolver: &TypeResolver) -> bool {
+ // NOTE: Take into account that while unions in C and C++ are copied by
+ // default, the may have an explicit destructor in C++, so we can't
+ // defer this check just for the union case.
+ if self.has_destructor(type_resolver) {
+ return false;
+ }
+
+ match self.kind {
+ CompKind::Union => true,
+ CompKind::Struct => {
+ // With template args, use a safe subset of the types,
+ // since copyability depends on the types itself.
+ self.ref_template.as_ref().map_or(true, |t| {
+ type_resolver.resolve_type(*t).can_derive_copy(type_resolver)
+ }) &&
+ self.base_members.iter().all(|t| {
+ type_resolver.resolve_type(*t).can_derive_copy(type_resolver)
+ }) &&
+ self.fields.iter().all(|field| {
+ type_resolver.resolve_type(field.ty)
+ .can_derive_copy(type_resolver)
+ })
+ }
+ }
+ }
+
+ pub fn is_template_specialization(&self) -> bool {
+ self.ref_template.is_some()
+ }
+
+ pub fn specialized_template(&self) -> Option<ItemId> {
+ self.ref_template
+ }
+
+ // Computes the layout of this type.
+ //
+ // This is called as a fallback under some circumstances where LLVM doesn't
+ // give us the correct layout.
+ // If we're a union without known layout, we try to compute it from our
+ // members. This is not ideal, but clang fails to report the size for
+ // these kind of unions, see test/headers/template_union.hpp
+ pub fn layout(&self, type_resolver: &TypeResolver) -> Option<Layout> {
+ use std::cmp;
+
+ // We can't do better than clang here, sorry.
+ if self.kind == CompKind::Struct {
+ return None;
+ }
+
+ let mut max_size = 0;
+ let mut max_align = 0;
+ for field in &self.fields {
+ let field_layout = type_resolver.resolve_type(field.ty)
+ .layout(type_resolver);
+
+ if let Some(layout) = field_layout {
+ max_size = cmp::max(max_size, layout.size);
+ max_align = cmp::max(max_align, layout.align);
+ }
+ }
+
+ Some(Layout::new(max_size, max_align))
+ }
+
+ pub fn fields(&self) -> &[Field] {
+ &self.fields
+ }
+
+ pub fn template_args(&self) -> &[ItemId] {
+ &self.template_args
+ }
+
+ pub fn has_non_type_template_params(&self) -> bool {
+ self.has_non_type_template_params
+ }
+
+ pub fn has_vtable(&self, type_resolver: &TypeResolver) -> bool {
+ self.has_vtable || self.base_members().iter().any(|base| {
+ type_resolver
+ .resolve_type(*base)
+ .has_vtable(type_resolver)
+ }) || self.ref_template.map_or(false, |template| {
+ type_resolver.resolve_type(template).has_vtable(type_resolver)
+ })
+ }
+
+ pub fn methods(&self) -> &[Method] {
+ &self.methods
+ }
+
+ pub fn kind(&self) -> CompKind {
+ self.kind
+ }
+
+ pub fn base_members(&self) -> &[ItemId] {
+ &self.base_members
+ }
+
+ pub fn from_ty(potential_id: ItemId,
+ ty: &clang::Type,
+ location: Option<clang::Cursor>,
+ ctx: &mut BindgenContext) -> Result<Self, ParseError> {
+ use clangll::*;
+ // Sigh... For class templates we want the location, for
+ // specialisations, we want the declaration... So just try both.
+ //
+ // TODO: Yeah, this code reads really bad.
+ let mut cursor = ty.declaration();
+ let mut kind = Self::kind_from_cursor(&cursor);
+ if kind.is_err() {
+ if let Some(location) = location {
+ kind = Self::kind_from_cursor(&location);
+ cursor = location;
+ }
+ }
+
+ let kind = try!(kind);
+
+ debug!("CompInfo::from_ty({:?}, {:?})", kind, cursor);
+
+
+ let mut ci = CompInfo::new(kind);
+ ci.is_anonymous = cursor.is_anonymous();
+ ci.template_args = match ty.num_template_args() {
+ // In forward declarations and not specializations, etc, they are in
+ // the ast, we'll meet them in CXCursor_TemplateTypeParameter
+ -1 => vec![],
+ len => {
+ let mut list = Vec::with_capacity(len as usize);
+ for i in 0..len {
+ let arg_type = ty.template_arg_type(i);
+ if arg_type.kind() != CXType_Invalid {
+ let type_id =
+ Item::from_ty_or_ref(arg_type, None, None, ctx);
+
+ list.push(type_id);
+ } else {
+ ci.has_non_type_template_params = true;
+ warn!("warning: Template parameter is not a type");
+ }
+ }
+
+ list
+ }
+ };
+
+ ci.ref_template = Item::parse(cursor.specialized(), None, ctx).ok();
+
+ let mut maybe_anonymous_struct_field = None;
+ cursor.visit(|cur, _other| {
+ if cur.kind() != CXCursor_FieldDecl {
+ if let Some((ty, ref _clang_ty)) = maybe_anonymous_struct_field {
+ let field = Field::new(None, ty, None, None, None, false);
+ ci.fields.push(field);
+ }
+ maybe_anonymous_struct_field = None;
+ }
+
+ match cur.kind() {
+ CXCursor_FieldDecl => {
+ match maybe_anonymous_struct_field.take() {
+ Some((ty, clang_ty)) => {
+ let mut used = false;
+ cur.visit(|child, _| {
+ if child.cur_type() == clang_ty {
+ used = true;
+ }
+ CXChildVisit_Continue
+ });
+ if !used {
+ let field = Field::new(None, ty, None, None, None, false);
+ ci.fields.push(field);
+ }
+ },
+ None => {}
+ }
+
+ let bit_width = cur.bit_width();
+ let field_type =
+ Item::from_ty_or_ref(cur.cur_type(), Some(*cur), Some(potential_id), ctx);
+ let comment = cur.raw_comment();
+ let annotations = Annotations::new(cur);
+ let name = cur.spelling();
+ let is_mutable = cursor.is_mutable_field();
+
+ // Name can be empty if there are bitfields, for example,
+ // see tests/headers/struct_with_bitfields.h
+ assert!(!name.is_empty() || bit_width.is_some(),
+ "Empty field name?");
+
+ let name = if name.is_empty() { None } else { Some(name) };
+
+ let field = Field::new(name, field_type, comment,
+ annotations, bit_width, is_mutable);
+ ci.fields.push(field);
+
+ // No we look for things like attributes and stuff.
+ cur.visit(|cur, _| {
+ if cur.kind() == CXCursor_UnexposedAttr {
+ ci.found_unknown_attr = true;
+ }
+ CXChildVisit_Continue
+ });
+
+ }
+ CXCursor_UnexposedAttr => {
+ ci.found_unknown_attr = true;
+ }
+ CXCursor_EnumDecl |
+ CXCursor_TypeAliasDecl |
+ CXCursor_TypedefDecl |
+ CXCursor_StructDecl |
+ CXCursor_UnionDecl |
+ CXCursor_ClassTemplate |
+ CXCursor_ClassDecl => {
+ let inner = Item::parse(*cur, Some(potential_id), ctx)
+ .expect("Inner ClassDecl");
+ if !ci.inner_types.contains(&inner) {
+ ci.inner_types.push(inner);
+ }
+ // A declaration of an union or a struct without name could
+ // also be an unnamed field, unfortunately.
+ if cur.spelling().is_empty() && cur.kind() != CXCursor_EnumDecl {
+ maybe_anonymous_struct_field = Some((inner, cur.cur_type()));
+ }
+ }
+ CXCursor_PackedAttr => {
+ ci.packed = true;
+ }
+ CXCursor_TemplateTypeParameter => {
+ // Yes! You can arrive here with an empty template parameter
+ // name! Awesome, isn't it?
+ //
+ // see tests/headers/empty_template_param_name.hpp
+ if cur.spelling().is_empty() {
+ return CXChildVisit_Continue;
+ }
+
+ let default_type =
+ Item::from_ty(&cur.cur_type(), Some(*cur), Some(potential_id), ctx).ok();
+
+ let param = Item::named_type(cur.spelling(), default_type, potential_id, ctx);
+ ci.template_args.push(param);
+ }
+ CXCursor_CXXBaseSpecifier => {
+ if !ci.has_vtable {
+ ci.has_vtable = cur.is_virtual_base();
+ }
+ let type_id = Item::from_ty(&cur.cur_type(), None, None, ctx)
+ .expect("BaseSpecifier");
+ ci.base_members.push(type_id);
+ }
+ CXCursor_CXXMethod => {
+ let is_virtual = cur.method_is_virtual();
+ let is_static = cur.method_is_static();
+ debug_assert!(!(is_static && is_virtual), "How?");
+
+ if !ci.has_vtable {
+ ci.has_vtable = is_virtual;
+ }
+
+ let linkage = cur.linkage();
+ if linkage != CXLinkage_External {
+ return CXChildVisit_Continue;
+ }
+
+ if cur.access_specifier() == CX_CXXPrivate {
+ return CXChildVisit_Continue;
+ }
+
+ let visibility = cur.visibility();
+ if visibility != CXVisibility_Default {
+ return CXChildVisit_Continue;
+ }
+
+ if cur.is_inlined_function() {
+ return CXChildVisit_Continue;
+ }
+
+ let spelling = cur.spelling();
+ if spelling.starts_with("operator") {
+ return CXChildVisit_Continue;
+ }
+
+ // This used to not be here, but then I tried generating
+ // stylo bindings with this (without path filters), and
+ // cried a lot with a method in gfx/Point.h
+ // (ToUnknownPoint), that somehow was causing the same type
+ // to be inserted in the map two times.
+ //
+ // I couldn't make a reduced test case, but anyway...
+ // Methods of template functions not only use to be inlined,
+ // but also instantiated, and we wouldn't be able to call
+ // them, so just bail out.
+ if !ci.template_args.is_empty() {
+ return CXChildVisit_Continue;
+ }
+
+ // NB: This gets us an owned `Function`, not a `FunctionSig`.
+ let method_signature = Item::parse(*cur, Some(potential_id), ctx)
+ .expect("CXXMethod");
+ let is_const = cur.method_is_const();
+ let method_kind = if is_static {
+ MethodKind::Static
+ } else if is_virtual {
+ MethodKind::Virtual
+ } else {
+ MethodKind::Normal
+ };
+ ci.methods.push(Method::new(method_kind, method_signature, is_const));
+ }
+ CXCursor_Destructor => {
+ if cur.method_is_virtual() {
+ // FIXME: Push to the method list?
+ ci.has_vtable = true;
+ }
+ ci.has_destructor = true;
+ }
+ CXCursor_NonTypeTemplateParameter => {
+ ci.has_non_type_template_params = true;
+ }
+ CXCursor_VarDecl => {
+ let linkage = cur.linkage();
+ if linkage != CXLinkage_External && linkage != CXLinkage_UniqueExternal {
+ return CXChildVisit_Continue;
+ }
+
+ let visibility = cur.visibility();
+ if visibility != CXVisibility_Default {
+ return CXChildVisit_Continue;
+ }
+
+ let item = Item::parse(*cur, Some(potential_id), ctx)
+ .expect("VarDecl");
+ ci.inner_vars.push(item);
+ }
+ // Intentionally not handled
+ CXCursor_CXXAccessSpecifier |
+ CXCursor_CXXFinalAttr |
+ CXCursor_Constructor |
+ CXCursor_FunctionTemplate |
+ CXCursor_ConversionFunction => {}
+ _ => {
+ warn!("unhandled composite member `{}` (kind {}) in `{}` ({})",
+ cur.spelling(), cur.kind(), cursor.spelling(),
+ cur.location());
+ }
+ }
+ CXChildVisit_Continue
+ });
+
+ if let Some((ty, _)) = maybe_anonymous_struct_field {
+ let field = Field::new(None, ty, None, None, None, false);
+ ci.fields.push(field);
+ }
+
+ Ok(ci)
+ }
+
+ fn kind_from_cursor(cursor: &clang::Cursor) -> Result<CompKind, ParseError> {
+ use clangll::*;
+ Ok(match cursor.kind() {
+ CXCursor_UnionDecl => CompKind::Union,
+ CXCursor_ClassDecl |
+ CXCursor_StructDecl => CompKind::Struct,
+ CXCursor_ClassTemplatePartialSpecialization |
+ CXCursor_ClassTemplate => {
+ match cursor.template_kind() {
+ CXCursor_UnionDecl => CompKind::Union,
+ _ => CompKind::Struct,
+ }
+ }
+ _ => {
+ warn!("Unknown kind for comp type: {:?}", cursor);
+ return Err(ParseError::Continue);
+ }
+ })
+ }
+
+ pub fn signature_contains_named_type(&self,
+ type_resolver: &TypeResolver,
+ ty: &Type) -> bool {
+ // We don't generate these, so rather don't make the codegen step to
+ // think we got it covered.
+ if self.has_non_type_template_params() {
+ return false;
+ }
+ self.template_args.iter().any(|arg| {
+ type_resolver.resolve_type(*arg)
+ .signature_contains_named_type(type_resolver, ty)
+ })
+ }
+
+ pub fn inner_types(&self) -> &[ItemId] {
+ &self.inner_types
+ }
+
+ pub fn inner_vars(&self) -> &[ItemId] {
+ &self.inner_vars
+ }
+
+ pub fn found_unknown_attr(&self) -> bool {
+ self.found_unknown_attr
+ }
+
+ pub fn packed(&self) -> bool {
+ self.packed
+ }
+
+ /// Returns whether this type needs an explicit vtable because it has
+ /// virtual methods and none of its base classes has already a vtable.
+ pub fn needs_explicit_vtable(&self, type_resolver: &TypeResolver) -> bool {
+ self.has_vtable(type_resolver) && !self.base_members.iter().any(|base| {
+ // NB: Ideally, we could rely in all these types being `comp`, and
+ // life would be beautiful.
+ //
+ // Unfortunately, given the way we implement --match-pat, and also
+ // that you can inherit from templated types, we need to handle
+ // other cases here too.
+ type_resolver
+ .resolve_type(*base)
+ .canonical_type(type_resolver)
+ .as_comp().map_or(false, |ci| {
+ ci.has_vtable(type_resolver)
+ })
+ })
+ }
+}