blob: a6f63a640ea4783e4369365035c09c60f5d61a8a [file] [log] [blame]
//! Intermediate representation for C/C++ functions and methods.
use super::comp::MethodKind;
use super::context::{BindgenContext, TypeId};
use super::dot::DotAttributes;
use super::item::Item;
use super::traversal::{EdgeKind, Trace, Tracer};
use super::ty::TypeKind;
use crate::clang;
use crate::parse::{
ClangItemParser, ClangSubItemParser, ParseError, ParseResult,
};
use clang_sys::{self, CXCallingConv};
use proc_macro2;
use quote;
use quote::TokenStreamExt;
use std::io;
const RUST_DERIVE_FUNPTR_LIMIT: usize = 12;
/// What kind of a function are we looking at?
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FunctionKind {
/// A plain, free function.
Function,
/// A method of some kind.
Method(MethodKind),
}
impl FunctionKind {
fn from_cursor(cursor: &clang::Cursor) -> Option<FunctionKind> {
// FIXME(emilio): Deduplicate logic with `ir::comp`.
Some(match cursor.kind() {
clang_sys::CXCursor_FunctionDecl => FunctionKind::Function,
clang_sys::CXCursor_Constructor => {
FunctionKind::Method(MethodKind::Constructor)
}
clang_sys::CXCursor_Destructor => {
FunctionKind::Method(if cursor.method_is_virtual() {
MethodKind::VirtualDestructor {
pure_virtual: cursor.method_is_pure_virtual(),
}
} else {
MethodKind::Destructor
})
}
clang_sys::CXCursor_CXXMethod => {
if cursor.method_is_virtual() {
FunctionKind::Method(MethodKind::Virtual {
pure_virtual: cursor.method_is_pure_virtual(),
})
} else if cursor.method_is_static() {
FunctionKind::Method(MethodKind::Static)
} else {
FunctionKind::Method(MethodKind::Normal)
}
}
_ => return None,
})
}
}
/// The style of linkage
#[derive(Debug, Clone, Copy)]
pub enum Linkage {
/// Externally visible and can be linked against
External,
/// Not exposed externally. 'static inline' functions will have this kind of linkage
Internal,
}
/// A function declaration, with a signature, arguments, and argument names.
///
/// The argument names vector must be the same length as the ones in the
/// signature.
#[derive(Debug)]
pub struct Function {
/// The name of this function.
name: String,
/// The mangled name, that is, the symbol.
mangled_name: Option<String>,
/// The id pointing to the current function signature.
signature: TypeId,
/// The doc comment on the function, if any.
comment: Option<String>,
/// The kind of function this is.
kind: FunctionKind,
/// The linkage of the function.
linkage: Linkage,
}
impl Function {
/// Construct a new function.
pub fn new(
name: String,
mangled_name: Option<String>,
signature: TypeId,
comment: Option<String>,
kind: FunctionKind,
linkage: Linkage,
) -> Self {
Function {
name,
mangled_name,
signature,
comment,
kind,
linkage,
}
}
/// Get this function's name.
pub fn name(&self) -> &str {
&self.name
}
/// Get this function's name.
pub fn mangled_name(&self) -> Option<&str> {
self.mangled_name.as_ref().map(|n| &**n)
}
/// Get this function's signature type.
pub fn signature(&self) -> TypeId {
self.signature
}
/// Get this function's kind.
pub fn kind(&self) -> FunctionKind {
self.kind
}
/// Get this function's linkage.
pub fn linkage(&self) -> Linkage {
self.linkage
}
}
impl DotAttributes for Function {
fn dot_attributes<W>(
&self,
_ctx: &BindgenContext,
out: &mut W,
) -> io::Result<()>
where
W: io::Write,
{
if let Some(ref mangled) = self.mangled_name {
let mangled: String =
mangled.chars().flat_map(|c| c.escape_default()).collect();
writeln!(
out,
"<tr><td>mangled name</td><td>{}</td></tr>",
mangled
)?;
}
Ok(())
}
}
/// An ABI extracted from a clang cursor.
#[derive(Debug, Copy, Clone)]
pub enum Abi {
/// The default C ABI.
C,
/// The "stdcall" ABI.
Stdcall,
/// The "fastcall" ABI.
Fastcall,
/// The "thiscall" ABI.
ThisCall,
/// The "aapcs" ABI.
Aapcs,
/// The "win64" ABI.
Win64,
/// An unknown or invalid ABI.
Unknown(CXCallingConv),
}
impl Abi {
/// Returns whether this Abi is known or not.
fn is_unknown(&self) -> bool {
match *self {
Abi::Unknown(..) => true,
_ => false,
}
}
}
impl quote::ToTokens for Abi {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.append_all(match *self {
Abi::C => quote! { "C" },
Abi::Stdcall => quote! { "stdcall" },
Abi::Fastcall => quote! { "fastcall" },
Abi::ThisCall => quote! { "thiscall" },
Abi::Aapcs => quote! { "aapcs" },
Abi::Win64 => quote! { "win64" },
Abi::Unknown(cc) => panic!(
"Cannot turn unknown calling convention to tokens: {:?}",
cc
),
});
}
}
/// A function signature.
#[derive(Debug)]
pub struct FunctionSig {
/// The return type of the function.
return_type: TypeId,
/// The type of the arguments, optionally with the name of the argument when
/// declared.
argument_types: Vec<(Option<String>, TypeId)>,
/// Whether this function is variadic.
is_variadic: bool,
/// Whether this function's return value must be used.
must_use: bool,
/// The ABI of this function.
abi: Abi,
}
fn get_abi(cc: CXCallingConv) -> Abi {
use clang_sys::*;
match cc {
CXCallingConv_Default => Abi::C,
CXCallingConv_C => Abi::C,
CXCallingConv_X86StdCall => Abi::Stdcall,
CXCallingConv_X86FastCall => Abi::Fastcall,
CXCallingConv_X86ThisCall => Abi::ThisCall,
CXCallingConv_AAPCS => Abi::Aapcs,
CXCallingConv_X86_64Win64 => Abi::Win64,
other => Abi::Unknown(other),
}
}
/// Get the mangled name for the cursor's referent.
pub fn cursor_mangling(
ctx: &BindgenContext,
cursor: &clang::Cursor,
) -> Option<String> {
if !ctx.options().enable_mangling {
return None;
}
// We early return here because libclang may crash in some case
// if we pass in a variable inside a partial specialized template.
// See rust-lang/rust-bindgen#67, and rust-lang/rust-bindgen#462.
if cursor.is_in_non_fully_specialized_template() {
return None;
}
let is_destructor = cursor.kind() == clang_sys::CXCursor_Destructor;
if let Ok(mut manglings) = cursor.cxx_manglings() {
while let Some(m) = manglings.pop() {
// Only generate the destructor group 1, see below.
if is_destructor && !m.ends_with("D1Ev") {
continue;
}
return Some(m);
}
}
let mut mangling = cursor.mangling();
if mangling.is_empty() {
return None;
}
if is_destructor {
// With old (3.8-) libclang versions, and the Itanium ABI, clang returns
// the "destructor group 0" symbol, which means that it'll try to free
// memory, which definitely isn't what we want.
//
// Explicitly force the destructor group 1 symbol.
//
// See http://refspecs.linuxbase.org/cxxabi-1.83.html#mangling-special
// for the reference, and http://stackoverflow.com/a/6614369/1091587 for
// a more friendly explanation.
//
// We don't need to do this for constructors since clang seems to always
// have returned the C1 constructor.
//
// FIXME(emilio): Can a legit symbol in other ABIs end with this string?
// I don't think so, but if it can this would become a linker error
// anyway, not an invalid free at runtime.
//
// TODO(emilio, #611): Use cpp_demangle if this becomes nastier with
// time.
if mangling.ends_with("D0Ev") {
let new_len = mangling.len() - 4;
mangling.truncate(new_len);
mangling.push_str("D1Ev");
}
}
Some(mangling)
}
fn args_from_ty_and_cursor(
ty: &clang::Type,
cursor: &clang::Cursor,
ctx: &mut BindgenContext,
) -> Vec<(Option<String>, TypeId)> {
let cursor_args = cursor.args().unwrap_or_default().into_iter();
let type_args = ty.args().unwrap_or_default().into_iter();
// Argument types can be found in either the cursor or the type, but argument names may only be
// found on the cursor. We often have access to both a type and a cursor for each argument, but
// in some cases we may only have one.
//
// Prefer using the type as the source of truth for the argument's type, but fall back to
// inspecting the cursor (this happens for Objective C interfaces).
//
// Prefer using the cursor for the argument's type, but fall back to using the parent's cursor
// (this happens for function pointer return types).
cursor_args
.map(Some)
.chain(std::iter::repeat(None))
.zip(type_args.map(Some).chain(std::iter::repeat(None)))
.take_while(|(cur, ty)| cur.is_some() || ty.is_some())
.map(|(arg_cur, arg_ty)| {
let name = arg_cur.map(|a| a.spelling()).and_then(|name| {
if name.is_empty() {
None
} else {
Some(name)
}
});
let cursor = arg_cur.unwrap_or(*cursor);
let ty = arg_ty.unwrap_or(cursor.cur_type());
(name, Item::from_ty_or_ref(ty, cursor, None, ctx))
})
.collect()
}
impl FunctionSig {
/// Construct a new function signature.
pub fn new(
return_type: TypeId,
argument_types: Vec<(Option<String>, TypeId)>,
is_variadic: bool,
must_use: bool,
abi: Abi,
) -> Self {
FunctionSig {
return_type,
argument_types,
is_variadic,
must_use,
abi: abi,
}
}
/// Construct a new function signature from the given Clang type.
pub fn from_ty(
ty: &clang::Type,
cursor: &clang::Cursor,
ctx: &mut BindgenContext,
) -> Result<Self, ParseError> {
use clang_sys::*;
debug!("FunctionSig::from_ty {:?} {:?}", ty, cursor);
// Skip function templates
let kind = cursor.kind();
if kind == CXCursor_FunctionTemplate {
return Err(ParseError::Continue);
}
let spelling = cursor.spelling();
// Don't parse operatorxx functions in C++
let is_operator = |spelling: &str| {
spelling.starts_with("operator") &&
!clang::is_valid_identifier(spelling)
};
if is_operator(&spelling) {
return Err(ParseError::Continue);
}
// Constructors of non-type template parameter classes for some reason
// include the template parameter in their name. Just skip them, since
// we don't handle well non-type template parameters anyway.
if (kind == CXCursor_Constructor || kind == CXCursor_Destructor) &&
spelling.contains('<')
{
return Err(ParseError::Continue);
}
let cursor = if cursor.is_valid() {
*cursor
} else {
ty.declaration()
};
let mut args = match kind {
CXCursor_FunctionDecl |
CXCursor_Constructor |
CXCursor_CXXMethod |
CXCursor_ObjCInstanceMethodDecl |
CXCursor_ObjCClassMethodDecl => {
args_from_ty_and_cursor(&ty, &cursor, ctx)
}
_ => {
// For non-CXCursor_FunctionDecl, visiting the cursor's children
// is the only reliable way to get parameter names.
let mut args = vec![];
cursor.visit(|c| {
if c.kind() == CXCursor_ParmDecl {
let ty =
Item::from_ty_or_ref(c.cur_type(), c, None, ctx);
let name = c.spelling();
let name =
if name.is_empty() { None } else { Some(name) };
args.push((name, ty));
}
CXChildVisit_Continue
});
if args.is_empty() {
// FIXME(emilio): Sometimes libclang doesn't expose the
// right AST for functions tagged as stdcall and such...
//
// https://bugs.llvm.org/show_bug.cgi?id=45919
args_from_ty_and_cursor(&ty, &cursor, ctx)
} else {
args
}
}
};
let must_use = ctx.options().enable_function_attribute_detection &&
cursor.has_warn_unused_result_attr();
let is_method = kind == CXCursor_CXXMethod;
let is_constructor = kind == CXCursor_Constructor;
let is_destructor = kind == CXCursor_Destructor;
if (is_constructor || is_destructor || is_method) &&
cursor.lexical_parent() != cursor.semantic_parent()
{
// Only parse constructors once.
return Err(ParseError::Continue);
}
if is_method || is_constructor || is_destructor {
let is_const = is_method && cursor.method_is_const();
let is_virtual = is_method && cursor.method_is_virtual();
let is_static = is_method && cursor.method_is_static();
if !is_static && !is_virtual {
let parent = cursor.semantic_parent();
let class = Item::parse(parent, None, ctx)
.expect("Expected to parse the class");
// The `class` most likely is not finished parsing yet, so use
// the unchecked variant.
let class = class.as_type_id_unchecked();
let class = if is_const {
let const_class_id = ctx.next_item_id();
ctx.build_const_wrapper(
const_class_id,
class,
None,
&parent.cur_type(),
)
} else {
class
};
let ptr =
Item::builtin_type(TypeKind::Pointer(class), false, ctx);
args.insert(0, (Some("this".into()), ptr));
} else if is_virtual {
let void = Item::builtin_type(TypeKind::Void, false, ctx);
let ptr =
Item::builtin_type(TypeKind::Pointer(void), false, ctx);
args.insert(0, (Some("this".into()), ptr));
}
}
let ty_ret_type = if kind == CXCursor_ObjCInstanceMethodDecl ||
kind == CXCursor_ObjCClassMethodDecl
{
ty.ret_type()
.or_else(|| cursor.ret_type())
.ok_or(ParseError::Continue)?
} else {
ty.ret_type().ok_or(ParseError::Continue)?
};
let ret = if is_constructor && ctx.is_target_wasm32() {
// Constructors in Clang wasm32 target return a pointer to the object
// being constructed.
let void = Item::builtin_type(TypeKind::Void, false, ctx);
Item::builtin_type(TypeKind::Pointer(void), false, ctx)
} else {
Item::from_ty_or_ref(ty_ret_type, cursor, None, ctx)
};
// Clang plays with us at "find the calling convention", see #549 and
// co. This seems to be a better fix than that commit.
let mut call_conv = ty.call_conv();
if let Some(ty) = cursor.cur_type().canonical_type().pointee_type() {
let cursor_call_conv = ty.call_conv();
if cursor_call_conv != CXCallingConv_Invalid {
call_conv = cursor_call_conv;
}
}
let abi = get_abi(call_conv);
if abi.is_unknown() {
warn!("Unknown calling convention: {:?}", call_conv);
}
Ok(Self::new(ret.into(), args, ty.is_variadic(), must_use, abi))
}
/// Get this function signature's return type.
pub fn return_type(&self) -> TypeId {
self.return_type
}
/// Get this function signature's argument (name, type) pairs.
pub fn argument_types(&self) -> &[(Option<String>, TypeId)] {
&self.argument_types
}
/// Get this function signature's ABI.
pub fn abi(&self) -> Abi {
self.abi
}
/// Is this function signature variadic?
pub fn is_variadic(&self) -> bool {
// Clang reports some functions as variadic when they *might* be
// variadic. We do the argument check because rust doesn't codegen well
// variadic functions without an initial argument.
self.is_variadic && !self.argument_types.is_empty()
}
/// Must this function's return value be used?
pub fn must_use(&self) -> bool {
self.must_use
}
/// Are function pointers with this signature able to derive Rust traits?
/// Rust only supports deriving traits for function pointers with a limited
/// number of parameters and a couple ABIs.
///
/// For more details, see:
///
/// * https://github.com/rust-lang/rust-bindgen/issues/547,
/// * https://github.com/rust-lang/rust/issues/38848,
/// * and https://github.com/rust-lang/rust/issues/40158
pub fn function_pointers_can_derive(&self) -> bool {
if self.argument_types.len() > RUST_DERIVE_FUNPTR_LIMIT {
return false;
}
match self.abi {
Abi::C | Abi::Unknown(..) => true,
_ => false,
}
}
}
impl ClangSubItemParser for Function {
fn parse(
cursor: clang::Cursor,
context: &mut BindgenContext,
) -> Result<ParseResult<Self>, ParseError> {
use clang_sys::*;
let kind = match FunctionKind::from_cursor(&cursor) {
None => return Err(ParseError::Continue),
Some(k) => k,
};
debug!("Function::parse({:?}, {:?})", cursor, cursor.cur_type());
let visibility = cursor.visibility();
if visibility != CXVisibility_Default {
return Err(ParseError::Continue);
}
if cursor.access_specifier() == CX_CXXPrivate {
return Err(ParseError::Continue);
}
if !context.options().generate_inline_functions &&
cursor.is_inlined_function()
{
return Err(ParseError::Continue);
}
let linkage = cursor.linkage();
let linkage = match linkage {
CXLinkage_External | CXLinkage_UniqueExternal => Linkage::External,
CXLinkage_Internal => Linkage::Internal,
_ => return Err(ParseError::Continue),
};
// Grab the signature using Item::from_ty.
let sig = Item::from_ty(&cursor.cur_type(), cursor, None, context)?;
let mut name = cursor.spelling();
assert!(!name.is_empty(), "Empty function name?");
if cursor.kind() == CXCursor_Destructor {
// Remove the leading `~`. The alternative to this is special-casing
// code-generation for destructor functions, which seems less than
// ideal.
if name.starts_with('~') {
name.remove(0);
}
// Add a suffix to avoid colliding with constructors. This would be
// technically fine (since we handle duplicated functions/methods),
// but seems easy enough to handle it here.
name.push_str("_destructor");
}
let mangled_name = cursor_mangling(context, &cursor);
let comment = cursor.raw_comment();
let function =
Self::new(name, mangled_name, sig, comment, kind, linkage);
Ok(ParseResult::New(function, Some(cursor)))
}
}
impl Trace for FunctionSig {
type Extra = ();
fn trace<T>(&self, _: &BindgenContext, tracer: &mut T, _: &())
where
T: Tracer,
{
tracer.visit_kind(self.return_type().into(), EdgeKind::FunctionReturn);
for &(_, ty) in self.argument_types() {
tracer.visit_kind(ty.into(), EdgeKind::FunctionParameter);
}
}
}