summaryrefslogtreecommitdiff
path: root/src/ir/ty.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ir/ty.rs')
-rw-r--r--src/ir/ty.rs537
1 files changed, 537 insertions, 0 deletions
diff --git a/src/ir/ty.rs b/src/ir/ty.rs
new file mode 100644
index 00000000..b0448437
--- /dev/null
+++ b/src/ir/ty.rs
@@ -0,0 +1,537 @@
+use super::comp::CompInfo;
+use super::enum_ty::Enum;
+use super::function::FunctionSig;
+use super::item::{Item, ItemId};
+use super::int::IntKind;
+use super::layout::Layout;
+use super::context::BindgenContext;
+use super::context::TypeResolver;
+use parse::{ClangItemParser, ParseResult, ParseError};
+use clang::{self, Cursor};
+
+#[derive(Debug)]
+pub struct Type {
+ /// The name of the type, or None if it was an unnamed struct or union.
+ name: Option<String>,
+ /// The layout of the type, if known.
+ layout: Option<Layout>,
+ /// Whether this type is marked as opaque.
+ opaque: bool,
+ /// Whether this type is marked as hidden.
+ hide: bool,
+ /// The inner kind of the type
+ kind: TypeKind,
+ /// Whether this type is const-qualified.
+ is_const: bool,
+}
+
+pub const RUST_DERIVE_IN_ARRAY_LIMIT: usize = 32usize;
+
+impl Type {
+ pub fn as_comp(&self) -> Option<&CompInfo> {
+ match self.kind {
+ TypeKind::Comp(ref ci) => Some(ci),
+ _ => None,
+ }
+ }
+
+ pub fn new(name: Option<String>,
+ layout: Option<Layout>,
+ kind: TypeKind,
+ is_const: bool) -> Self {
+ Type {
+ name: name,
+ layout: layout,
+ opaque: false,
+ hide: false,
+ kind: kind,
+ is_const: is_const,
+ }
+ }
+
+ pub fn kind(&self) -> &TypeKind {
+ &self.kind
+ }
+
+ pub fn kind_mut(&mut self) -> &mut TypeKind {
+ &mut self.kind
+ }
+
+ pub fn name(&self) -> Option<&str> {
+ self.name.as_ref().map(|name| &**name)
+ }
+
+ pub fn is_comp(&self) -> bool {
+ match self.kind {
+ TypeKind::Comp(..) => true,
+ _ => false,
+ }
+ }
+
+ pub fn is_named(&self) -> bool {
+ match self.kind {
+ TypeKind::Named(..) => true,
+ _ => false,
+ }
+ }
+
+ pub fn is_builtin_or_named(&self) -> bool {
+ match self.kind {
+ TypeKind::Void |
+ TypeKind::NullPtr |
+ TypeKind::Function(..) |
+ TypeKind::Array(..) |
+ TypeKind::Reference(..) |
+ TypeKind::Pointer(..) |
+ TypeKind::Int(..) |
+ TypeKind::Float(..) |
+ TypeKind::Named(..) => true,
+ _ => false,
+ }
+ }
+
+ /// Creates a new named type, with name `name`.
+ pub fn named(name: String, default: Option<ItemId>) -> Self {
+ assert!(!name.is_empty());
+ // TODO: stop duplicating the name, it's stupid.
+ let kind = TypeKind::Named(name.clone(), default);
+ Self::new(Some(name), None, kind, false)
+ }
+
+ pub fn is_integer_literal(&self) -> bool {
+ match *self.kind() {
+ TypeKind::Int(..) => true,
+ _ => false,
+ }
+ }
+
+ pub fn is_const(&self) -> bool {
+ self.is_const
+ }
+
+ pub fn layout(&self, type_resolver: &TypeResolver) -> Option<Layout> {
+ use std::mem;
+
+ self.layout.or_else(|| {
+ match self.kind {
+ TypeKind::Comp(ref ci)
+ => ci.layout(type_resolver),
+ // FIXME(emilio): This is a hack for anonymous union templates.
+ // Use the actual pointer size!
+ TypeKind::Pointer(..)
+ => Some(Layout::new(mem::size_of::<*mut ()>(), mem::align_of::<*mut ()>())),
+ TypeKind::ResolvedTypeRef(inner)
+ => type_resolver.resolve_type(inner).layout(type_resolver),
+ _ => None,
+ }
+ })
+ }
+
+ pub fn is_opaque(&self, _type_resolver: &TypeResolver) -> bool {
+ self.opaque
+ }
+
+ pub fn can_derive_debug(&self, type_resolver: &TypeResolver) -> bool {
+ !self.is_opaque(type_resolver) && match self.kind {
+ TypeKind::Array(t, len) => {
+ len <= RUST_DERIVE_IN_ARRAY_LIMIT &&
+ type_resolver.resolve_type(t).can_derive_debug(type_resolver)
+ }
+ TypeKind::ResolvedTypeRef(t) |
+ TypeKind::Alias(_, t) => {
+ type_resolver.resolve_type(t).can_derive_debug(type_resolver)
+ }
+ TypeKind::Comp(ref info) => {
+ info.can_derive_debug(type_resolver, self.layout(type_resolver))
+ }
+ _ => true,
+ }
+ }
+
+ // For some reason, deriving copies of an array of a type that is not known
+ // to be copy is a compile error. e.g.:
+ //
+ // #[derive(Copy)]
+ // struct A<T> {
+ // member: T,
+ // }
+ //
+ // is fine, while:
+ //
+ // #[derive(Copy)]
+ // struct A<T> {
+ // member: [T; 1],
+ // }
+ //
+ // is an error.
+ //
+ // That's the point of the existence of can_derive_copy_in_array().
+ pub fn can_derive_copy_in_array(&self, type_resolver: &TypeResolver) -> bool {
+ match self.kind {
+ TypeKind::ResolvedTypeRef(t) |
+ TypeKind::Alias(_, t) |
+ TypeKind::Array(t, _) => {
+ type_resolver.resolve_type(t)
+ .can_derive_copy_in_array(type_resolver)
+ }
+ TypeKind::Named(..) => false,
+ _ => self.can_derive_copy(type_resolver),
+ }
+ }
+
+ pub fn can_derive_copy(&self, type_resolver: &TypeResolver) -> bool {
+ !self.is_opaque(type_resolver) && match self.kind {
+ TypeKind::Array(t, len) => {
+ len <= RUST_DERIVE_IN_ARRAY_LIMIT &&
+ type_resolver.resolve_type(t).can_derive_copy_in_array(type_resolver)
+ }
+ TypeKind::ResolvedTypeRef(t) |
+ TypeKind::TemplateRef(t, _) |
+ TypeKind::Alias(_, t) => {
+ type_resolver.resolve_type(t).can_derive_copy(type_resolver)
+ }
+ TypeKind::Comp(ref info) => {
+ info.can_derive_copy(type_resolver)
+ }
+ _ => true,
+ }
+ }
+
+ pub fn has_vtable(&self, type_resolver: &TypeResolver) -> bool {
+ // FIXME: Can we do something about template parameters? Huh...
+ match self.kind {
+ TypeKind::TemplateRef(t, _) |
+ TypeKind::Alias(_, t) |
+ TypeKind::ResolvedTypeRef(t) |
+ TypeKind::Array(t, _) => {
+ type_resolver.resolve_type(t).has_vtable(type_resolver)
+ }
+ TypeKind::Comp(ref info) => {
+ info.has_vtable(type_resolver)
+ }
+ _ => false,
+ }
+
+ }
+
+ pub fn has_destructor(&self, type_resolver: &TypeResolver) -> bool {
+ self.is_opaque(type_resolver) || match self.kind {
+ TypeKind::TemplateRef(t, _) |
+ TypeKind::Alias(_, t) |
+ TypeKind::ResolvedTypeRef(t) |
+ TypeKind::Array(t, _) => {
+ type_resolver.resolve_type(t).has_destructor(type_resolver)
+ }
+ TypeKind::Comp(ref info) => {
+ info.has_destructor(type_resolver)
+ }
+ _ => false,
+ }
+ }
+
+ pub fn signature_contains_named_type(&self,
+ type_resolver: &TypeResolver,
+ ty: &Type) -> bool {
+ debug_assert!(ty.is_named());
+ let name = match *ty.kind() {
+ TypeKind::Named(ref name, _) => name,
+ _ => unreachable!(),
+ };
+
+ match self.kind {
+ TypeKind::Named(ref this_name, _)
+ => this_name == name,
+ TypeKind::ResolvedTypeRef(t) |
+ TypeKind::Array(t, _) |
+ TypeKind::Pointer(t)
+ => type_resolver.resolve_type(t)
+ .signature_contains_named_type(type_resolver, ty),
+ TypeKind::TemplateRef(_inner, ref template_args) => {
+ template_args.iter().any(|arg| {
+ type_resolver.resolve_type(*arg)
+ .signature_contains_named_type(type_resolver, ty)
+ })
+ }
+ TypeKind::Comp(ref ci)
+ => ci.signature_contains_named_type(type_resolver, ty),
+ _ => false,
+ }
+ }
+
+ pub fn canonical_type<'tr>(&'tr self, type_resolver: &'tr TypeResolver) -> &'tr Type {
+ match self.kind {
+ TypeKind::Named(..) |
+ TypeKind::Array(..) |
+ TypeKind::Comp(..) |
+ TypeKind::Int(..) |
+ TypeKind::Float(..) |
+ TypeKind::Function(..) |
+ TypeKind::Enum(..) |
+ TypeKind::Reference(..) |
+ TypeKind::Void |
+ TypeKind::NullPtr |
+ TypeKind::Pointer(..) => self,
+
+ TypeKind::ResolvedTypeRef(inner) |
+ TypeKind::Alias(_, inner) |
+ TypeKind::TemplateRef(inner, _)
+ => type_resolver.resolve_type(inner).canonical_type(type_resolver),
+
+ TypeKind::UnresolvedTypeRef(..)
+ => unreachable!("Should have been resolved after parsing!"),
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum FloatKind {
+ Float,
+ Double,
+ LongDouble,
+}
+
+/// The different kinds of types that we can parse.
+///
+/// TODO: The name in the Alias and Named kinds is a bit unsound, should be in
+/// type.name?
+#[derive(Debug)]
+pub enum TypeKind {
+ /// The void type.
+ Void,
+ /// The nullptr_t type.
+ NullPtr,
+ /// A compound type, that is, a class, struct, or union.
+ Comp(CompInfo),
+ /// An integer type, of a given kind. `bool` and `char` are also considered
+ /// integers.
+ Int(IntKind),
+ /// A floating point type.
+ Float(FloatKind),
+ /// A type alias, with a name, that points to another type.
+ Alias(String, ItemId),
+ /// An array of a type and a lenght.
+ Array(ItemId, usize),
+ /// A function type, with a given signature.
+ Function(FunctionSig),
+ /// An enum type.
+ Enum(Enum),
+ /// A pointer to a type. The bool field represents whether it's const or
+ /// not.
+ Pointer(ItemId),
+ /// A reference to a type, as in: int& foo().
+ Reference(ItemId),
+ /// A reference to a template, with different template parameter names. To
+ /// see why this is needed, check out the creation of this variant in
+ /// `Type::from_clang_ty`.
+ TemplateRef(ItemId, Vec<ItemId>),
+
+ /// A reference to a yet-to-resolve type. This stores the clang cursor
+ /// itself, and postpones its resolution.
+ ///
+ /// These are gone in a phase after parsing where these are mapped to
+ /// already known types, and are converted to ResolvedTypeRef.
+ ///
+ /// see tests/headers/typeref.hpp to see somewhere where this is a problem.
+ UnresolvedTypeRef(clang::Type, Option<clang::Cursor>),
+ ResolvedTypeRef(ItemId),
+
+ /// A named type, that is, a template parameter, with an optional default
+ /// type.
+ Named(String, Option<ItemId>),
+}
+
+impl Type {
+ pub fn is_unsized(&self, type_resolver: &TypeResolver) -> bool {
+ match self.kind {
+ TypeKind::Void => true,
+ TypeKind::Comp(ref ci) => ci.is_unsized(type_resolver),
+ TypeKind::Array(inner, size) => {
+ size == 0 ||
+ type_resolver.resolve_type(inner).is_unsized(type_resolver)
+ }
+ TypeKind::ResolvedTypeRef(inner) |
+ TypeKind::Alias(_, inner) |
+ TypeKind::TemplateRef(inner, _)
+ => type_resolver.resolve_type(inner).is_unsized(type_resolver),
+ TypeKind::Named(..) |
+ TypeKind::Int(..) |
+ TypeKind::Float(..) |
+ TypeKind::Function(..) |
+ TypeKind::Enum(..) |
+ TypeKind::Reference(..) |
+ TypeKind::NullPtr |
+ TypeKind::Pointer(..) => false,
+
+ TypeKind::UnresolvedTypeRef(..)
+ => unreachable!("Should have been resolved after parsing!"),
+ }
+ }
+
+ pub fn from_clang_ty(potential_id: ItemId,
+ ty: &clang::Type,
+ location: Option<Cursor>,
+ parent_id: Option<ItemId>,
+ ctx: &mut BindgenContext) -> Result<ParseResult<Self>, ParseError> {
+ use clangll::*;
+ if let Some(ty) = ctx.builtin_or_resolved_ty(parent_id, ty, location) {
+ debug!("{:?} already resolved: {:?}", ty, location);
+ return Ok(ParseResult::AlreadyResolved(ty));
+ }
+
+ let layout = ty.fallible_layout().ok();
+ let cursor = ty.declaration();
+ let mut name = cursor.spelling();
+
+ debug!("from_clang_ty: {:?}, ty: {:?}, loc: {:?}", potential_id, ty, location);
+ debug!("currently_parsed_types: {:?}", ctx.currently_parsed_types);
+
+ let canonical_ty = ty.canonical_type();
+ let kind = match ty.kind() {
+ CXType_Unexposed if *ty != canonical_ty &&
+ canonical_ty.kind() != CXType_Invalid => {
+ debug!("Looking for canonical type: {:?}", canonical_ty);
+ return Self::from_clang_ty(potential_id, &canonical_ty,
+ location, parent_id, ctx);
+ }
+ CXType_Unexposed |
+ CXType_Invalid => {
+ // For some reason Clang doesn't give us any hint
+ // in some situations where we should generate a
+ // function pointer (see
+ // tests/headers/func_ptr_in_struct.h), so we do a
+ // guess here trying to see if it has a valid return
+ // type.
+ if ty.ret_type().kind() != CXType_Invalid {
+ let signature =
+ try!(FunctionSig::from_ty(ty, &location.unwrap_or(cursor), ctx));
+ TypeKind::Function(signature)
+ // Same here, with template specialisations we can safely assume
+ // this is a Comp(..)
+ } else if ty.num_template_args() > 0 {
+ debug!("Template specialization: {:?}", ty);
+ let complex =
+ CompInfo::from_ty(potential_id, ty, location, ctx)
+ .expect("C'mon");
+ TypeKind::Comp(complex)
+ } else if let Some(location) = location {
+ match location.kind() {
+ CXCursor_ClassTemplatePartialSpecialization |
+ CXCursor_ClassTemplate => {
+ name = location.spelling();
+ let complex =
+ CompInfo::from_ty(potential_id, ty, Some(location), ctx)
+ .expect("C'mon");
+ TypeKind::Comp(complex)
+ }
+ CXCursor_TemplateRef => {
+ let referenced = location.referenced();
+ return Self::from_clang_ty(potential_id,
+ &referenced.cur_type(),
+ Some(referenced),
+ parent_id,
+ ctx);
+ }
+ CXCursor_TypeRef => {
+ let referenced = location.referenced();
+ // FIXME: use potential id?
+ return Ok(ParseResult::AlreadyResolved(
+ Item::from_ty_or_ref(referenced.cur_type(),
+ Some(referenced),
+ parent_id,
+ ctx)));
+ }
+ _ => {
+ if ty.kind() == CXType_Unexposed {
+ warn!("Unexposed type {:?}, recursing inside, loc: {:?}", ty, location);
+ return Err(ParseError::Recurse);
+ }
+
+ error!("invalid type {:?}", ty);
+ return Err(ParseError::Continue);
+ }
+ }
+ } else {
+ // TODO: Don't duplicate this!
+ if ty.kind() == CXType_Unexposed {
+ warn!("Unexposed type {:?}, recursing inside", ty);
+ return Err(ParseError::Recurse);
+ }
+
+ error!("invalid type `{}`", ty.spelling());
+ return Err(ParseError::Continue);
+ }
+ }
+ // NOTE: We don't resolve pointers eagerly because the pointee type
+ // might not have been parsed, and if it contains templates or
+ // something else we might get confused, see the comment inside
+ // TypeRef.
+ //
+ // We might need to, though, if the context is already in the
+ // process of resolving them.
+ CXType_MemberPointer |
+ CXType_Pointer => {
+ let inner =
+ Item::from_ty_or_ref(ty.pointee_type(), Some(ty.pointee_type().declaration()), parent_id, ctx);
+ TypeKind::Pointer(inner)
+ }
+ // XXX: RValueReference is most likely wrong, but I don't think we
+ // can even add bindings for that, so huh.
+ CXType_RValueReference |
+ CXType_LValueReference => {
+ let inner =
+ Item::from_ty_or_ref(ty.pointee_type(), Some(ty.pointee_type().declaration()), parent_id, ctx);
+ TypeKind::Reference(inner)
+ }
+ // XXX DependentSizedArray is wrong
+ CXType_VariableArray |
+ CXType_DependentSizedArray |
+ CXType_IncompleteArray => {
+ let inner = Item::from_ty(&ty.elem_type(), location, parent_id, ctx)
+ .expect("Not able to resolve array element?");
+ TypeKind::Pointer(inner)
+ }
+ CXType_FunctionNoProto |
+ CXType_FunctionProto => {
+ let signature = try!(FunctionSig::from_ty(ty, &location.unwrap_or(cursor), ctx));
+ TypeKind::Function(signature)
+ }
+ CXType_Typedef => {
+ let inner = cursor.typedef_type();
+ let inner =
+ Item::from_ty_or_ref(inner, location, parent_id, ctx);
+ TypeKind::Alias(ty.spelling(), inner)
+ }
+ CXType_Enum => {
+ let enum_ = Enum::from_ty(ty, ctx)
+ .expect("Not an enum?");
+ TypeKind::Enum(enum_)
+ }
+ CXType_Record => {
+ let complex = CompInfo::from_ty(potential_id, ty, location, ctx)
+ .expect("Not a complex type?");
+ TypeKind::Comp(complex)
+ }
+ CXType_ConstantArray => {
+ let inner = Item::from_ty(&ty.elem_type(), location, parent_id, ctx)
+ .expect("Not able to resolve array element?");
+ TypeKind::Array(inner, ty.array_size())
+ }
+ #[cfg(not(feature="llvm_stable"))]
+ CXType_Elaborated => {
+ return Self::from_clang_ty(potential_id, &ty.named(),
+ location, parent_id, ctx);
+ }
+ _ => {
+ error!("unsupported type {:?} at {:?}", ty, location);
+ return Err(ParseError::Continue);
+ }
+ };
+
+ let name = if name.is_empty() { None } else { Some(name) };
+ let is_const = ty.is_const();
+
+ let ty = Type::new(name, layout, kind, is_const);
+ // TODO: maybe declaration.canonical()?
+ Ok(ParseResult::New(ty, Some(cursor.canonical())))
+ }
+}