839 lines
27 KiB
Rust
839 lines
27 KiB
Rust
//! 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::callbacks::{ItemInfo, ItemKind};
|
|
use crate::clang::{self, ABIKind, Attribute};
|
|
use crate::parse::{ClangSubItemParser, ParseError, ParseResult};
|
|
use clang_sys::{self, CXCallingConv};
|
|
|
|
use quote::TokenStreamExt;
|
|
use std::io;
|
|
use std::str::FromStr;
|
|
|
|
const RUST_DERIVE_FUNPTR_LIMIT: usize = 12;
|
|
|
|
/// What kind of a function are we looking at?
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
pub(crate) enum FunctionKind {
|
|
/// A plain, free function.
|
|
Function,
|
|
/// A method of some kind.
|
|
Method(MethodKind),
|
|
}
|
|
|
|
impl FunctionKind {
|
|
/// Given a clang cursor, return the kind of function it represents, or
|
|
/// `None` otherwise.
|
|
pub(crate) 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(crate) 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(crate) struct Function {
|
|
/// The name of this function.
|
|
name: String,
|
|
|
|
/// The mangled name, that is, the symbol.
|
|
mangled_name: Option<String>,
|
|
|
|
/// The link name. If specified, overwrite mangled_name.
|
|
link_name: Option<String>,
|
|
|
|
/// The ID pointing to the current function signature.
|
|
signature: TypeId,
|
|
|
|
/// The kind of function this is.
|
|
kind: FunctionKind,
|
|
|
|
/// The linkage of the function.
|
|
linkage: Linkage,
|
|
}
|
|
|
|
impl Function {
|
|
/// Construct a new function.
|
|
pub(crate) fn new(
|
|
name: String,
|
|
mangled_name: Option<String>,
|
|
link_name: Option<String>,
|
|
signature: TypeId,
|
|
kind: FunctionKind,
|
|
linkage: Linkage,
|
|
) -> Self {
|
|
Function {
|
|
name,
|
|
mangled_name,
|
|
link_name,
|
|
signature,
|
|
kind,
|
|
linkage,
|
|
}
|
|
}
|
|
|
|
/// Get this function's name.
|
|
pub(crate) fn name(&self) -> &str {
|
|
&self.name
|
|
}
|
|
|
|
/// Get this function's name.
|
|
pub(crate) fn mangled_name(&self) -> Option<&str> {
|
|
self.mangled_name.as_deref()
|
|
}
|
|
|
|
/// Get this function's link name.
|
|
pub fn link_name(&self) -> Option<&str> {
|
|
self.link_name.as_deref()
|
|
}
|
|
|
|
/// Get this function's signature type.
|
|
pub(crate) fn signature(&self) -> TypeId {
|
|
self.signature
|
|
}
|
|
|
|
/// Get this function's kind.
|
|
pub(crate) fn kind(&self) -> FunctionKind {
|
|
self.kind
|
|
}
|
|
|
|
/// Get this function's linkage.
|
|
pub(crate) 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(())
|
|
}
|
|
}
|
|
|
|
/// A valid rust ABI.
|
|
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
|
|
pub enum Abi {
|
|
/// The default C ABI.
|
|
C,
|
|
/// The "stdcall" ABI.
|
|
Stdcall,
|
|
/// The "efiapi" ABI.
|
|
EfiApi,
|
|
/// The "fastcall" ABI.
|
|
Fastcall,
|
|
/// The "thiscall" ABI.
|
|
ThisCall,
|
|
/// The "vectorcall" ABI.
|
|
Vectorcall,
|
|
/// The "aapcs" ABI.
|
|
Aapcs,
|
|
/// The "win64" ABI.
|
|
Win64,
|
|
/// The "C-unwind" ABI.
|
|
CUnwind,
|
|
/// The "system" ABI.
|
|
System,
|
|
}
|
|
|
|
impl FromStr for Abi {
|
|
type Err = String;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s {
|
|
"C" => Ok(Self::C),
|
|
"stdcall" => Ok(Self::Stdcall),
|
|
"efiapi" => Ok(Self::EfiApi),
|
|
"fastcall" => Ok(Self::Fastcall),
|
|
"thiscall" => Ok(Self::ThisCall),
|
|
"vectorcall" => Ok(Self::Vectorcall),
|
|
"aapcs" => Ok(Self::Aapcs),
|
|
"win64" => Ok(Self::Win64),
|
|
"C-unwind" => Ok(Self::CUnwind),
|
|
"system" => Ok(Self::System),
|
|
_ => Err(format!("Invalid or unknown ABI {:?}", s)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for Abi {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let s = match *self {
|
|
Self::C => "C",
|
|
Self::Stdcall => "stdcall",
|
|
Self::EfiApi => "efiapi",
|
|
Self::Fastcall => "fastcall",
|
|
Self::ThisCall => "thiscall",
|
|
Self::Vectorcall => "vectorcall",
|
|
Self::Aapcs => "aapcs",
|
|
Self::Win64 => "win64",
|
|
Self::CUnwind => "C-unwind",
|
|
Abi::System => "system",
|
|
};
|
|
|
|
s.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl quote::ToTokens for Abi {
|
|
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
|
let abi = self.to_string();
|
|
tokens.append_all(quote! { #abi });
|
|
}
|
|
}
|
|
|
|
/// An ABI extracted from a clang cursor.
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub(crate) enum ClangAbi {
|
|
/// An ABI known by Rust.
|
|
Known(Abi),
|
|
/// An unknown or invalid ABI.
|
|
Unknown(CXCallingConv),
|
|
}
|
|
|
|
impl ClangAbi {
|
|
/// Returns whether this Abi is known or not.
|
|
fn is_unknown(&self) -> bool {
|
|
matches!(*self, ClangAbi::Unknown(..))
|
|
}
|
|
}
|
|
|
|
impl quote::ToTokens for ClangAbi {
|
|
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
|
match *self {
|
|
Self::Known(abi) => abi.to_tokens(tokens),
|
|
Self::Unknown(cc) => panic!(
|
|
"Cannot turn unknown calling convention to tokens: {:?}",
|
|
cc
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A function signature.
|
|
#[derive(Debug)]
|
|
pub(crate) struct FunctionSig {
|
|
/// The name of this function signature.
|
|
name: String,
|
|
|
|
/// 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,
|
|
is_divergent: bool,
|
|
|
|
/// Whether this function's return value must be used.
|
|
must_use: bool,
|
|
|
|
/// The ABI of this function.
|
|
abi: ClangAbi,
|
|
}
|
|
|
|
fn get_abi(cc: CXCallingConv) -> ClangAbi {
|
|
use clang_sys::*;
|
|
match cc {
|
|
CXCallingConv_Default => ClangAbi::Known(Abi::C),
|
|
CXCallingConv_C => ClangAbi::Known(Abi::C),
|
|
CXCallingConv_X86StdCall => ClangAbi::Known(Abi::Stdcall),
|
|
CXCallingConv_X86FastCall => ClangAbi::Known(Abi::Fastcall),
|
|
CXCallingConv_X86ThisCall => ClangAbi::Known(Abi::ThisCall),
|
|
CXCallingConv_X86VectorCall => ClangAbi::Known(Abi::Vectorcall),
|
|
CXCallingConv_AAPCS => ClangAbi::Known(Abi::Aapcs),
|
|
CXCallingConv_X86_64Win64 => ClangAbi::Known(Abi::Win64),
|
|
CXCallingConv_AArch64VectorCall => ClangAbi::Known(Abi::Vectorcall),
|
|
other => ClangAbi::Unknown(other),
|
|
}
|
|
}
|
|
|
|
/// Get the mangled name for the cursor's referent.
|
|
pub(crate) 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_itanium_abi = ctx.abi_kind() == ABIKind::GenericItanium;
|
|
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_itanium_abi && is_destructor && !m.ends_with("D1Ev") {
|
|
continue;
|
|
}
|
|
|
|
return Some(m);
|
|
}
|
|
}
|
|
|
|
let mut mangling = cursor.mangling();
|
|
if mangling.is_empty() {
|
|
return None;
|
|
}
|
|
|
|
if is_itanium_abi && 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_else(|| cursor.cur_type());
|
|
(name, Item::from_ty_or_ref(ty, cursor, None, ctx))
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
impl FunctionSig {
|
|
/// Get the function name.
|
|
pub(crate) fn name(&self) -> &str {
|
|
&self.name
|
|
}
|
|
|
|
/// Construct a new function signature from the given Clang type.
|
|
pub(crate) 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, mut is_divergent) =
|
|
if ctx.options().enable_function_attribute_detection {
|
|
let [must_use, no_return, no_return_cpp] = cursor.has_attrs(&[
|
|
Attribute::MUST_USE,
|
|
Attribute::NO_RETURN,
|
|
Attribute::NO_RETURN_CPP,
|
|
]);
|
|
(must_use, no_return || no_return_cpp)
|
|
} else {
|
|
Default::default()
|
|
};
|
|
|
|
// Check if the type contains __attribute__((noreturn)) outside of parentheses. This is
|
|
// somewhat fragile, but it seems to be the only way to get at this information as of
|
|
// libclang 9.
|
|
let ty_spelling = ty.spelling();
|
|
let has_attribute_noreturn = ty_spelling
|
|
.match_indices("__attribute__((noreturn))")
|
|
.any(|(i, _)| {
|
|
let depth = ty_spelling[..i]
|
|
.bytes()
|
|
.filter_map(|ch| match ch {
|
|
b'(' => Some(1),
|
|
b')' => Some(-1),
|
|
_ => None,
|
|
})
|
|
.sum::<isize>();
|
|
depth == 0
|
|
});
|
|
is_divergent = is_divergent || has_attribute_noreturn;
|
|
|
|
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 {
|
|
name: spelling,
|
|
return_type: ret,
|
|
argument_types: args,
|
|
is_variadic: ty.is_variadic(),
|
|
is_divergent,
|
|
must_use,
|
|
abi,
|
|
})
|
|
}
|
|
|
|
/// Get this function signature's return type.
|
|
pub(crate) fn return_type(&self) -> TypeId {
|
|
self.return_type
|
|
}
|
|
|
|
/// Get this function signature's argument (name, type) pairs.
|
|
pub(crate) fn argument_types(&self) -> &[(Option<String>, TypeId)] {
|
|
&self.argument_types
|
|
}
|
|
|
|
/// Get this function signature's ABI.
|
|
pub(crate) fn abi(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
name: Option<&str>,
|
|
) -> crate::codegen::error::Result<ClangAbi> {
|
|
// FIXME (pvdrz): Try to do this check lazily instead. Maybe store the ABI inside `ctx`
|
|
// instead?.
|
|
let abi = if let Some(name) = name {
|
|
if let Some((abi, _)) = ctx
|
|
.options()
|
|
.abi_overrides
|
|
.iter()
|
|
.find(|(_, regex_set)| regex_set.matches(name))
|
|
{
|
|
ClangAbi::Known(*abi)
|
|
} else {
|
|
self.abi
|
|
}
|
|
} else if let Some((abi, _)) = ctx
|
|
.options()
|
|
.abi_overrides
|
|
.iter()
|
|
.find(|(_, regex_set)| regex_set.matches(&self.name))
|
|
{
|
|
ClangAbi::Known(*abi)
|
|
} else {
|
|
self.abi
|
|
};
|
|
|
|
match abi {
|
|
ClangAbi::Known(Abi::ThisCall)
|
|
if !ctx.options().rust_features().thiscall_abi =>
|
|
{
|
|
Err(crate::codegen::error::Error::UnsupportedAbi("thiscall"))
|
|
}
|
|
ClangAbi::Known(Abi::Vectorcall)
|
|
if !ctx.options().rust_features().vectorcall_abi =>
|
|
{
|
|
Err(crate::codegen::error::Error::UnsupportedAbi("vectorcall"))
|
|
}
|
|
ClangAbi::Known(Abi::CUnwind)
|
|
if !ctx.options().rust_features().c_unwind_abi =>
|
|
{
|
|
Err(crate::codegen::error::Error::UnsupportedAbi("C-unwind"))
|
|
}
|
|
ClangAbi::Known(Abi::EfiApi)
|
|
if !ctx.options().rust_features().abi_efiapi =>
|
|
{
|
|
Err(crate::codegen::error::Error::UnsupportedAbi("efiapi"))
|
|
}
|
|
ClangAbi::Known(Abi::Win64) if self.is_variadic() => {
|
|
Err(crate::codegen::error::Error::UnsupportedAbi("Win64"))
|
|
}
|
|
abi => Ok(abi),
|
|
}
|
|
}
|
|
|
|
/// Is this function signature variadic?
|
|
pub(crate) 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(crate) 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(crate) fn function_pointers_can_derive(&self) -> bool {
|
|
if self.argument_types.len() > RUST_DERIVE_FUNPTR_LIMIT {
|
|
return false;
|
|
}
|
|
|
|
matches!(self.abi, ClangAbi::Known(Abi::C) | ClangAbi::Unknown(..))
|
|
}
|
|
|
|
/// Whether this function has attributes marking it as divergent.
|
|
pub(crate) fn is_divergent(&self) -> bool {
|
|
self.is_divergent
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
let linkage = cursor.linkage();
|
|
let linkage = match linkage {
|
|
CXLinkage_External | CXLinkage_UniqueExternal => Linkage::External,
|
|
CXLinkage_Internal => Linkage::Internal,
|
|
_ => return Err(ParseError::Continue),
|
|
};
|
|
|
|
if cursor.is_inlined_function() ||
|
|
cursor
|
|
.definition()
|
|
.map_or(false, |x| x.is_inlined_function())
|
|
{
|
|
if !context.options().generate_inline_functions &&
|
|
!context.options().wrap_static_fns
|
|
{
|
|
return Err(ParseError::Continue);
|
|
}
|
|
|
|
if cursor.is_deleted_function() {
|
|
return Err(ParseError::Continue);
|
|
}
|
|
|
|
// We cannot handle `inline` functions that are not `static`.
|
|
if context.options().wrap_static_fns &&
|
|
cursor.is_inlined_function() &&
|
|
matches!(linkage, Linkage::External)
|
|
{
|
|
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");
|
|
}
|
|
if let Some(nm) = context.options().last_callback(|callbacks| {
|
|
callbacks.generated_name_override(ItemInfo {
|
|
name: name.as_str(),
|
|
kind: ItemKind::Function,
|
|
})
|
|
}) {
|
|
name = nm;
|
|
}
|
|
assert!(!name.is_empty(), "Empty function name.");
|
|
|
|
let mangled_name = cursor_mangling(context, &cursor);
|
|
|
|
let link_name = context.options().last_callback(|callbacks| {
|
|
callbacks.generated_link_name_override(ItemInfo {
|
|
name: name.as_str(),
|
|
kind: ItemKind::Function,
|
|
})
|
|
});
|
|
|
|
let function = Self::new(
|
|
name.clone(),
|
|
mangled_name,
|
|
link_name,
|
|
sig,
|
|
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);
|
|
}
|
|
}
|
|
}
|