Skip to content

Some function pointers, typedefs, and OSX's stdlib. #58

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 22, 2016
Merged
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
13 changes: 11 additions & 2 deletions src/clang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,23 @@ pub struct Cursor {

impl fmt::Debug for Cursor {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "Cursor({} kind: {}, loc: {})",
self.spelling(), kind_to_str(self.kind()), self.location())
write!(fmt, "Cursor({} kind: {}, loc: {}, usr: {:?})",
self.spelling(), kind_to_str(self.kind()), self.location(), self.usr())
}
}

pub type CursorVisitor<'s> = for<'a, 'b> FnMut(&'a Cursor, &'b Cursor) -> Enum_CXChildVisitResult + 's;

impl Cursor {
pub fn usr(&self) -> Option<String> {
let s = String_ { x: unsafe { clang_getCursorUSR(self.x) } }.to_string();
if s.is_empty() {
None
} else {
Some(s)
}
}

pub fn is_declaration(&self) -> bool {
unsafe { clang_isDeclaration(self.kind()) != 0 }
}
Expand Down
21 changes: 20 additions & 1 deletion src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ impl CodeGenerator for Type {
TypeKind::Float(..) |
TypeKind::Array(..) |
TypeKind::Pointer(..) |
TypeKind::BlockPointer |
TypeKind::Reference(..) |
TypeKind::TemplateRef(..) |
TypeKind::Function(..) |
Expand Down Expand Up @@ -1306,6 +1307,11 @@ impl ToRustTy for Type {
IntKind::ULongLong => raw!(c_ulonglong),
IntKind::U16 => aster::ty::TyBuilder::new().u16(),
IntKind::U32 => aster::ty::TyBuilder::new().u32(),
// FIXME: This doesn't generate the proper alignment, but we
// can't do better right now. We should be able to use
// i128/u128 when they're available.
IntKind::U128 |
IntKind::I128 => ArrayTyBuilder::new().with_len(2).build(aster::ty::TyBuilder::new().u64()),
}
}
TypeKind::Float(fk) => {
Expand Down Expand Up @@ -1377,10 +1383,23 @@ impl ToRustTy for Type {

utils::build_templated_path(item, ctx, false)
}
TypeKind::BlockPointer => {
let void = raw!(c_void);
void.to_ptr(/* is_const = */ false, ctx.span())
}
TypeKind::Pointer(inner) |
TypeKind::Reference(inner) => {
let inner = ctx.resolve_item(inner);
inner.to_rust_ty(ctx).to_ptr(inner.expect_type().is_const(), ctx.span())
let inner_ty = inner.expect_type();
let ty = inner.to_rust_ty(ctx);

// Avoid the first function pointer level, since it's already
// represented in Rust.
if inner_ty.canonical_type(ctx).is_function() {
ty
} else {
ty.to_ptr(inner.expect_type().is_const(), ctx.span())
}
}
TypeKind::Named(..) => {
let name = item.canonical_name(ctx);
Expand Down
82 changes: 59 additions & 23 deletions src/ir/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ use syntax::ext::base::ExtCtxt;
use parse::ClangItemParser;
use BindgenOptions;

/// A key used to index a resolved type, so we only process it once.
///
/// This is almost always a USR string (an unique identifier generated by
/// clang), but it can also be the canonical declaration if the type is unnamed,
/// in which case clang may generate the same USR for multiple nested unnamed
/// types.
#[derive(Eq, PartialEq, Hash, Debug)]
enum TypeKey {
USR(String),
Declaration(Cursor),
}

// This is just convenience to avoid creating a manual debug impl for the
// context.
struct GenContext<'ctx>(ExtCtxt<'ctx>);
Expand All @@ -33,11 +45,9 @@ pub struct BindgenContext<'ctx> {
/// output.
items: BTreeMap<ItemId, Item>,

/// Clang cursor to type map. This is needed to be able to associate types
/// with item ids during parsing.
///
/// The cursor used for storage is the definition cursor.
types: HashMap<Cursor, ItemId>,
/// Clang USR to type map. This is needed to be able to associate types with
/// item ids during parsing.
types: HashMap<TypeKey, ItemId>,

/// A cursor to module map. Similar reason than above.
modules: HashMap<Cursor, ItemId>,
Expand Down Expand Up @@ -131,29 +141,46 @@ impl<'ctx> BindgenContext<'ctx> {

let id = item.id();
let is_type = item.kind().is_type();
let is_unnamed = is_type && item.expect_type().name().is_none();
let old_item = self.items.insert(id, item);
assert!(old_item.is_none(), "Inserted type twice?");

// Unnamed items can have an USR, but they can't be referenced from
// other sites explicitly and the USR can match if the unnamed items are
// nested, so don't bother tracking them.
if is_type && declaration.is_some() {
let declaration = declaration.unwrap();
debug_assert_eq!(declaration, declaration.canonical());
if declaration.is_valid() {
let old = self.types.insert(declaration, id);
debug_assert_eq!(old, None);
} else if location.is_some() &&
(location.unwrap().kind() == CXCursor_ClassTemplate ||
location.unwrap().kind() == CXCursor_ClassTemplatePartialSpecialization) {
let old = self.types.insert(location.unwrap().canonical(), id);
debug_assert_eq!(old, None);
} else {
let mut declaration = declaration.unwrap();
if !declaration.is_valid() {
if let Some(location) = location {
if location.kind() == CXCursor_ClassTemplate ||
location.kind() == CXCursor_ClassTemplatePartialSpecialization {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: maybe use a match here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I want to use the matches! crate instead, here and in a few other places, but I'll do that in a follow-up.

declaration = location;
}
}
}
declaration = declaration.canonical();
if !declaration.is_valid() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you rebound declaration to the canonical one, which is invalid.

What does the debug! message end up saying, given the declaration you passed to it is the invalid one now?

// This could happen, for example, with types like `int*` or
// similar.
//
// Fortunately, we don't care about those types being
// duplicated, so we can just ignore them.
debug!("Invalid declaration {:?} found for type {:?}",
declaration, self.items.get(&id).unwrap().kind().expect_type());
return;
}

let key = if is_unnamed {
TypeKey::Declaration(declaration)
} else if let Some(usr) = declaration.usr() {
TypeKey::USR(usr)
} else {
error!("Valid declaration with no USR: {:?}, {:?}", declaration, location);
return;
};

let old = self.types.insert(key, id);
debug_assert_eq!(old, None);
}
}

Expand Down Expand Up @@ -206,7 +233,7 @@ impl<'ctx> BindgenContext<'ctx> {
self.collected_typerefs
}

fn collect_typerefs(&mut self) -> Vec<(ItemId, clang::Type, Option<clang::Cursor>)> {
fn collect_typerefs(&mut self) -> Vec<(ItemId, clang::Type, Option<clang::Cursor>, Option<ItemId>)> {
debug_assert!(!self.collected_typerefs);
self.collected_typerefs = true;
let mut typerefs = vec![];
Expand All @@ -218,8 +245,8 @@ impl<'ctx> BindgenContext<'ctx> {
};

match *ty.kind() {
TypeKind::UnresolvedTypeRef(ref ty, loc) => {
typerefs.push((*id, ty.clone(), loc));
TypeKind::UnresolvedTypeRef(ref ty, loc, parent_id) => {
typerefs.push((*id, ty.clone(), loc, parent_id));
}
_ => {},
};
Expand All @@ -230,9 +257,9 @@ impl<'ctx> BindgenContext<'ctx> {
fn resolve_typerefs(&mut self) {
let typerefs = self.collect_typerefs();

for (id, ty, loc) in typerefs {
for (id, ty, loc, parent_id) in typerefs {
let _resolved = {
let resolved = Item::from_ty(&ty, loc, None, self)
let resolved = Item::from_ty(&ty, loc, parent_id, self)
.expect("What happened?");
let mut item = self.items.get_mut(&id).unwrap();

Expand Down Expand Up @@ -494,8 +521,15 @@ impl<'ctx> BindgenContext<'ctx> {
}
let canonical_declaration = declaration.canonical();
if canonical_declaration.is_valid() {
// First lookup to see if we already have it resolved.
let id = self.types.get(&canonical_declaration).map(|id| *id);
let id =
self.types.get(&TypeKey::Declaration(canonical_declaration))
.map(|id| *id)
.or_else(|| {
canonical_declaration.usr().and_then(|usr| {
self.types.get(&TypeKey::USR(usr))
})
.map(|id| *id)
});
if let Some(id) = id {
debug!("Already resolved ty {:?}, {:?}, {:?} {:?}",
id, declaration, ty, location);
Expand Down Expand Up @@ -572,6 +606,8 @@ impl<'ctx> BindgenContext<'ctx> {
CXType_ULong => TypeKind::Int(IntKind::ULong),
CXType_LongLong => TypeKind::Int(IntKind::LongLong),
CXType_ULongLong => TypeKind::Int(IntKind::ULongLong),
CXType_Int128 => TypeKind::Int(IntKind::I128),
CXType_UInt128 => TypeKind::Int(IntKind::U128),
CXType_Float => TypeKind::Float(FloatKind::Float),
CXType_Double => TypeKind::Float(FloatKind::Double),
CXType_LongDouble => TypeKind::Float(FloatKind::LongDouble),
Expand Down
6 changes: 4 additions & 2 deletions src/ir/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub enum IntKind {
ULongLong,
U16, // For Char16 and Wchar
U32, // For Char32
I128,
U128,
// Though now we're at it we could add equivalents for the rust types...
}

Expand All @@ -21,10 +23,10 @@ impl IntKind {
use self::IntKind::*;
match *self {
Bool | UChar | UShort |
UInt | ULong | ULongLong | U16 | U32 => false,
UInt | ULong | ULongLong | U16 | U32 | U128 => false,

Char | Short | Int |
Long | LongLong => true,
Long | LongLong | I128 => true,
}
}
}
30 changes: 23 additions & 7 deletions src/ir/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,19 @@ impl Item {
TypeKind::Array(inner, _) |
TypeKind::Pointer(inner) |
TypeKind::Reference(inner) |
TypeKind::Alias(_, inner) |
TypeKind::ResolvedTypeRef(inner) => {
ctx.resolve_item(inner).applicable_template_args(ctx)
}
TypeKind::Alias(_, inner) => {
let parent_args = ctx.resolve_item(self.parent_id())
.applicable_template_args(ctx);
let inner = ctx.resolve_type(inner);
// Avoid unused type parameters, sigh.
parent_args.iter().cloned().filter(|arg| {
let arg = ctx.resolve_type(*arg);
arg.is_named() && inner.signature_contains_named_type(ctx, arg)
}).collect()
}
// XXX Is this completely correct? Partial template specialization
// is hard anyways, sigh...
TypeKind::TemplateRef(_, ref args) => {
Expand Down Expand Up @@ -436,11 +445,19 @@ impl ClangItemParser for Item {
location: Option<clang::Cursor>,
parent_id: Option<ItemId>,
context: &mut BindgenContext) -> ItemId {
debug!("from_ty_or_ref: {:?}, {:?}, {:?}", ty, location, parent_id);
Self::from_ty_or_ref_with_id(ItemId::next(), ty, location, parent_id, context)
}

fn from_ty_or_ref_with_id(potential_id: ItemId,
ty: clang::Type,
location: Option<clang::Cursor>,
parent_id: Option<ItemId>,
context: &mut BindgenContext) -> ItemId {
debug!("from_ty_or_ref_with_id: {:?} {:?}, {:?}, {:?}", potential_id, ty, location, parent_id);

if context.collected_typerefs() {
debug!("refs already collected, resolving directly");
return Self::from_ty(&ty, location, parent_id, context)
return Self::from_ty_with_id(potential_id, &ty, location, parent_id, context)
.expect("Unable to resolve type");
}

Expand All @@ -452,15 +469,14 @@ impl ClangItemParser for Item {
debug!("New unresolved type reference: {:?}, {:?}", ty, location);

let is_const = ty.is_const();
let kind = TypeKind::UnresolvedTypeRef(ty, location);
let id = ItemId::next();
let kind = TypeKind::UnresolvedTypeRef(ty, location, parent_id);
let current_module = context.current_module();
context.add_item(Item::new(id, None, None,
context.add_item(Item::new(potential_id, None, None,
parent_id.unwrap_or(current_module),
ItemKind::Type(Type::new(None, None, kind, is_const))),
Some(clang::Cursor::null()),
None);
id
potential_id
}


Expand Down
Loading