forked from mirrors/gecko-dev
Backed out changeset c0d256e9cde9 (bug 1866014) Backed out changeset e7d101bd73d9 (bug 1670633) Backed out changeset a849a8e4bd37 (bug 1670633)
5437 lines
178 KiB
Rust
5437 lines
178 KiB
Rust
mod dyngen;
|
|
pub(crate) mod error;
|
|
|
|
mod helpers;
|
|
mod impl_debug;
|
|
mod impl_partialeq;
|
|
mod postprocessing;
|
|
mod serialize;
|
|
pub(crate) mod struct_layout;
|
|
|
|
#[cfg(test)]
|
|
#[allow(warnings)]
|
|
pub(crate) mod bitfield_unit;
|
|
#[cfg(all(test, target_endian = "little"))]
|
|
mod bitfield_unit_tests;
|
|
|
|
use self::dyngen::DynamicItems;
|
|
use self::helpers::attributes;
|
|
use self::struct_layout::StructLayoutTracker;
|
|
|
|
use super::BindgenOptions;
|
|
|
|
use crate::callbacks::{DeriveInfo, FieldInfo, TypeKind as DeriveTypeKind};
|
|
use crate::ir::analysis::{HasVtable, Sizedness};
|
|
use crate::ir::annotations::{
|
|
Annotations, FieldAccessorKind, FieldVisibilityKind,
|
|
};
|
|
use crate::ir::comp::{
|
|
Bitfield, BitfieldUnit, CompInfo, CompKind, Field, FieldData, FieldMethods,
|
|
Method, MethodKind,
|
|
};
|
|
use crate::ir::context::{BindgenContext, ItemId};
|
|
use crate::ir::derive::{
|
|
CanDerive, CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveEq,
|
|
CanDeriveHash, CanDeriveOrd, CanDerivePartialEq, CanDerivePartialOrd,
|
|
};
|
|
use crate::ir::dot;
|
|
use crate::ir::enum_ty::{Enum, EnumVariant, EnumVariantValue};
|
|
use crate::ir::function::{
|
|
ClangAbi, Function, FunctionKind, FunctionSig, Linkage,
|
|
};
|
|
use crate::ir::int::IntKind;
|
|
use crate::ir::item::{IsOpaque, Item, ItemCanonicalName, ItemCanonicalPath};
|
|
use crate::ir::item_kind::ItemKind;
|
|
use crate::ir::layout::Layout;
|
|
use crate::ir::module::Module;
|
|
use crate::ir::objc::{ObjCInterface, ObjCMethod};
|
|
use crate::ir::template::{
|
|
AsTemplateParam, TemplateInstantiation, TemplateParameters,
|
|
};
|
|
use crate::ir::ty::{Type, TypeKind};
|
|
use crate::ir::var::Var;
|
|
|
|
use proc_macro2::{self, Ident, Span};
|
|
use quote::TokenStreamExt;
|
|
|
|
use crate::{Entry, HashMap, HashSet};
|
|
use std::borrow::Cow;
|
|
use std::cell::Cell;
|
|
use std::collections::VecDeque;
|
|
use std::ffi::CStr;
|
|
use std::fmt::{self, Write};
|
|
use std::ops;
|
|
use std::str::{self, FromStr};
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
pub enum CodegenError {
|
|
Serialize { msg: String, loc: String },
|
|
Io(String),
|
|
}
|
|
|
|
impl From<std::io::Error> for CodegenError {
|
|
fn from(err: std::io::Error) -> Self {
|
|
Self::Io(err.to_string())
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for CodegenError {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Serialize { msg, loc } => {
|
|
write!(f, "serialization error at {}: {}", loc, msg)
|
|
}
|
|
Self::Io(err) => err.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
// Name of type defined in constified enum module
|
|
pub(crate) static CONSTIFIED_ENUM_MODULE_REPR_NAME: &str = "Type";
|
|
|
|
fn top_level_path(
|
|
ctx: &BindgenContext,
|
|
item: &Item,
|
|
) -> Vec<proc_macro2::TokenStream> {
|
|
let mut path = vec![quote! { self }];
|
|
|
|
if ctx.options().enable_cxx_namespaces {
|
|
for _ in 0..item.codegen_depth(ctx) {
|
|
path.push(quote! { super });
|
|
}
|
|
}
|
|
|
|
path
|
|
}
|
|
|
|
fn root_import(
|
|
ctx: &BindgenContext,
|
|
module: &Item,
|
|
) -> proc_macro2::TokenStream {
|
|
assert!(ctx.options().enable_cxx_namespaces, "Somebody messed it up");
|
|
assert!(module.is_module());
|
|
|
|
let mut path = top_level_path(ctx, module);
|
|
|
|
let root = ctx.root_module().canonical_name(ctx);
|
|
let root_ident = ctx.rust_ident(root);
|
|
path.push(quote! { #root_ident });
|
|
|
|
let mut tokens = quote! {};
|
|
tokens.append_separated(path, quote!(::));
|
|
|
|
quote! {
|
|
#[allow(unused_imports)]
|
|
use #tokens ;
|
|
}
|
|
}
|
|
|
|
bitflags! {
|
|
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
|
struct DerivableTraits: u16 {
|
|
const DEBUG = 1 << 0;
|
|
const DEFAULT = 1 << 1;
|
|
const COPY = 1 << 2;
|
|
const CLONE = 1 << 3;
|
|
const HASH = 1 << 4;
|
|
const PARTIAL_ORD = 1 << 5;
|
|
const ORD = 1 << 6;
|
|
const PARTIAL_EQ = 1 << 7;
|
|
const EQ = 1 << 8;
|
|
}
|
|
}
|
|
|
|
fn derives_of_item(
|
|
item: &Item,
|
|
ctx: &BindgenContext,
|
|
packed: bool,
|
|
) -> DerivableTraits {
|
|
let mut derivable_traits = DerivableTraits::empty();
|
|
|
|
let all_template_params = item.all_template_params(ctx);
|
|
|
|
if item.can_derive_copy(ctx) && !item.annotations().disallow_copy() {
|
|
derivable_traits |= DerivableTraits::COPY;
|
|
|
|
if ctx.options().rust_features().builtin_clone_impls ||
|
|
!all_template_params.is_empty()
|
|
{
|
|
// FIXME: This requires extra logic if you have a big array in a
|
|
// templated struct. The reason for this is that the magic:
|
|
// fn clone(&self) -> Self { *self }
|
|
// doesn't work for templates.
|
|
//
|
|
// It's not hard to fix though.
|
|
derivable_traits |= DerivableTraits::CLONE;
|
|
}
|
|
} else if packed {
|
|
// If the struct or union is packed, deriving from Copy is required for
|
|
// deriving from any other trait.
|
|
return derivable_traits;
|
|
}
|
|
|
|
if item.can_derive_debug(ctx) && !item.annotations().disallow_debug() {
|
|
derivable_traits |= DerivableTraits::DEBUG;
|
|
}
|
|
|
|
if item.can_derive_default(ctx) && !item.annotations().disallow_default() {
|
|
derivable_traits |= DerivableTraits::DEFAULT;
|
|
}
|
|
|
|
if item.can_derive_hash(ctx) {
|
|
derivable_traits |= DerivableTraits::HASH;
|
|
}
|
|
|
|
if item.can_derive_partialord(ctx) {
|
|
derivable_traits |= DerivableTraits::PARTIAL_ORD;
|
|
}
|
|
|
|
if item.can_derive_ord(ctx) {
|
|
derivable_traits |= DerivableTraits::ORD;
|
|
}
|
|
|
|
if item.can_derive_partialeq(ctx) {
|
|
derivable_traits |= DerivableTraits::PARTIAL_EQ;
|
|
}
|
|
|
|
if item.can_derive_eq(ctx) {
|
|
derivable_traits |= DerivableTraits::EQ;
|
|
}
|
|
|
|
derivable_traits
|
|
}
|
|
|
|
impl From<DerivableTraits> for Vec<&'static str> {
|
|
fn from(derivable_traits: DerivableTraits) -> Vec<&'static str> {
|
|
[
|
|
(DerivableTraits::DEBUG, "Debug"),
|
|
(DerivableTraits::DEFAULT, "Default"),
|
|
(DerivableTraits::COPY, "Copy"),
|
|
(DerivableTraits::CLONE, "Clone"),
|
|
(DerivableTraits::HASH, "Hash"),
|
|
(DerivableTraits::PARTIAL_ORD, "PartialOrd"),
|
|
(DerivableTraits::ORD, "Ord"),
|
|
(DerivableTraits::PARTIAL_EQ, "PartialEq"),
|
|
(DerivableTraits::EQ, "Eq"),
|
|
]
|
|
.iter()
|
|
.filter_map(|&(flag, derive)| {
|
|
Some(derive).filter(|_| derivable_traits.contains(flag))
|
|
})
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
struct WrapAsVariadic {
|
|
new_name: String,
|
|
idx_of_va_list_arg: usize,
|
|
}
|
|
|
|
struct CodegenResult<'a> {
|
|
items: Vec<proc_macro2::TokenStream>,
|
|
dynamic_items: DynamicItems,
|
|
|
|
/// A monotonic counter used to add stable unique ID's to stuff that doesn't
|
|
/// need to be referenced by anything.
|
|
codegen_id: &'a Cell<usize>,
|
|
|
|
/// Whether a bindgen union has been generated at least once.
|
|
saw_bindgen_union: bool,
|
|
|
|
/// Whether an incomplete array has been generated at least once.
|
|
saw_incomplete_array: bool,
|
|
|
|
/// Whether Objective C types have been seen at least once.
|
|
saw_objc: bool,
|
|
|
|
/// Whether Apple block types have been seen at least once.
|
|
saw_block: bool,
|
|
|
|
/// Whether a bitfield allocation unit has been seen at least once.
|
|
saw_bitfield_unit: bool,
|
|
|
|
items_seen: HashSet<ItemId>,
|
|
/// The set of generated function/var names, needed because in C/C++ is
|
|
/// legal to do something like:
|
|
///
|
|
/// ```c++
|
|
/// extern "C" {
|
|
/// void foo();
|
|
/// extern int bar;
|
|
/// }
|
|
///
|
|
/// extern "C" {
|
|
/// void foo();
|
|
/// extern int bar;
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// Being these two different declarations.
|
|
functions_seen: HashSet<String>,
|
|
vars_seen: HashSet<String>,
|
|
|
|
/// Used for making bindings to overloaded functions. Maps from a canonical
|
|
/// function name to the number of overloads we have already codegen'd for
|
|
/// that name. This lets us give each overload a unique suffix.
|
|
overload_counters: HashMap<String, u32>,
|
|
|
|
/// List of items to serialize. With optionally the argument for the wrap as
|
|
/// variadic transformation to be applied.
|
|
items_to_serialize: Vec<(ItemId, Option<WrapAsVariadic>)>,
|
|
}
|
|
|
|
impl<'a> CodegenResult<'a> {
|
|
fn new(codegen_id: &'a Cell<usize>) -> Self {
|
|
CodegenResult {
|
|
items: vec![],
|
|
dynamic_items: DynamicItems::new(),
|
|
saw_bindgen_union: false,
|
|
saw_incomplete_array: false,
|
|
saw_objc: false,
|
|
saw_block: false,
|
|
saw_bitfield_unit: false,
|
|
codegen_id,
|
|
items_seen: Default::default(),
|
|
functions_seen: Default::default(),
|
|
vars_seen: Default::default(),
|
|
overload_counters: Default::default(),
|
|
items_to_serialize: Default::default(),
|
|
}
|
|
}
|
|
|
|
fn dynamic_items(&mut self) -> &mut DynamicItems {
|
|
&mut self.dynamic_items
|
|
}
|
|
|
|
fn saw_bindgen_union(&mut self) {
|
|
self.saw_bindgen_union = true;
|
|
}
|
|
|
|
fn saw_incomplete_array(&mut self) {
|
|
self.saw_incomplete_array = true;
|
|
}
|
|
|
|
fn saw_objc(&mut self) {
|
|
self.saw_objc = true;
|
|
}
|
|
|
|
fn saw_block(&mut self) {
|
|
self.saw_block = true;
|
|
}
|
|
|
|
fn saw_bitfield_unit(&mut self) {
|
|
self.saw_bitfield_unit = true;
|
|
}
|
|
|
|
fn seen<Id: Into<ItemId>>(&self, item: Id) -> bool {
|
|
self.items_seen.contains(&item.into())
|
|
}
|
|
|
|
fn set_seen<Id: Into<ItemId>>(&mut self, item: Id) {
|
|
self.items_seen.insert(item.into());
|
|
}
|
|
|
|
fn seen_function(&self, name: &str) -> bool {
|
|
self.functions_seen.contains(name)
|
|
}
|
|
|
|
fn saw_function(&mut self, name: &str) {
|
|
self.functions_seen.insert(name.into());
|
|
}
|
|
|
|
/// Get the overload number for the given function name. Increments the
|
|
/// counter internally so the next time we ask for the overload for this
|
|
/// name, we get the incremented value, and so on.
|
|
fn overload_number(&mut self, name: &str) -> u32 {
|
|
let counter = self.overload_counters.entry(name.into()).or_insert(0);
|
|
let number = *counter;
|
|
*counter += 1;
|
|
number
|
|
}
|
|
|
|
fn seen_var(&self, name: &str) -> bool {
|
|
self.vars_seen.contains(name)
|
|
}
|
|
|
|
fn saw_var(&mut self, name: &str) {
|
|
self.vars_seen.insert(name.into());
|
|
}
|
|
|
|
fn inner<F>(&mut self, cb: F) -> Vec<proc_macro2::TokenStream>
|
|
where
|
|
F: FnOnce(&mut Self),
|
|
{
|
|
let mut new = Self::new(self.codegen_id);
|
|
|
|
cb(&mut new);
|
|
|
|
self.saw_incomplete_array |= new.saw_incomplete_array;
|
|
self.saw_objc |= new.saw_objc;
|
|
self.saw_block |= new.saw_block;
|
|
self.saw_bitfield_unit |= new.saw_bitfield_unit;
|
|
self.saw_bindgen_union |= new.saw_bindgen_union;
|
|
|
|
new.items
|
|
}
|
|
}
|
|
|
|
impl<'a> ops::Deref for CodegenResult<'a> {
|
|
type Target = Vec<proc_macro2::TokenStream>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.items
|
|
}
|
|
}
|
|
|
|
impl<'a> ops::DerefMut for CodegenResult<'a> {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.items
|
|
}
|
|
}
|
|
|
|
/// A trait to convert a rust type into a pointer, optionally const, to the same
|
|
/// type.
|
|
trait ToPtr {
|
|
fn to_ptr(self, is_const: bool) -> syn::Type;
|
|
}
|
|
|
|
impl ToPtr for syn::Type {
|
|
fn to_ptr(self, is_const: bool) -> syn::Type {
|
|
if is_const {
|
|
syn::parse_quote! { *const #self }
|
|
} else {
|
|
syn::parse_quote! { *mut #self }
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An extension trait for `syn::Type` that lets us append any implicit
|
|
/// template parameters that exist for some type, if necessary.
|
|
trait WithImplicitTemplateParams {
|
|
fn with_implicit_template_params(
|
|
self,
|
|
ctx: &BindgenContext,
|
|
item: &Item,
|
|
) -> Self;
|
|
}
|
|
|
|
impl WithImplicitTemplateParams for syn::Type {
|
|
fn with_implicit_template_params(
|
|
self,
|
|
ctx: &BindgenContext,
|
|
item: &Item,
|
|
) -> Self {
|
|
let item = item.id().into_resolver().through_type_refs().resolve(ctx);
|
|
|
|
let params = match *item.expect_type().kind() {
|
|
TypeKind::UnresolvedTypeRef(..) => {
|
|
unreachable!("already resolved unresolved type refs")
|
|
}
|
|
TypeKind::ResolvedTypeRef(..) => {
|
|
unreachable!("we resolved item through type refs")
|
|
}
|
|
// None of these types ever have implicit template parameters.
|
|
TypeKind::Void |
|
|
TypeKind::NullPtr |
|
|
TypeKind::Pointer(..) |
|
|
TypeKind::Reference(..) |
|
|
TypeKind::Int(..) |
|
|
TypeKind::Float(..) |
|
|
TypeKind::Complex(..) |
|
|
TypeKind::Array(..) |
|
|
TypeKind::TypeParam |
|
|
TypeKind::Opaque |
|
|
TypeKind::Function(..) |
|
|
TypeKind::Enum(..) |
|
|
TypeKind::ObjCId |
|
|
TypeKind::ObjCSel |
|
|
TypeKind::TemplateInstantiation(..) => None,
|
|
_ => {
|
|
let params = item.used_template_params(ctx);
|
|
if params.is_empty() {
|
|
None
|
|
} else {
|
|
Some(params.into_iter().map(|p| {
|
|
p.try_to_rust_ty(ctx, &()).expect(
|
|
"template params cannot fail to be a rust type",
|
|
)
|
|
}))
|
|
}
|
|
}
|
|
};
|
|
|
|
if let Some(params) = params {
|
|
syn::parse_quote! { #self<#(#params),*> }
|
|
} else {
|
|
self
|
|
}
|
|
}
|
|
}
|
|
|
|
trait CodeGenerator {
|
|
/// Extra information from the caller.
|
|
type Extra;
|
|
|
|
/// Extra information returned to the caller.
|
|
type Return;
|
|
|
|
fn codegen(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
result: &mut CodegenResult<'_>,
|
|
extra: &Self::Extra,
|
|
) -> Self::Return;
|
|
}
|
|
|
|
impl Item {
|
|
fn process_before_codegen(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
result: &mut CodegenResult,
|
|
) -> bool {
|
|
if !self.is_enabled_for_codegen(ctx) {
|
|
return false;
|
|
}
|
|
|
|
if self.is_blocklisted(ctx) || result.seen(self.id()) {
|
|
debug!(
|
|
"<Item as CodeGenerator>::process_before_codegen: Ignoring hidden or seen: \
|
|
self = {:?}",
|
|
self
|
|
);
|
|
return false;
|
|
}
|
|
|
|
if !ctx.codegen_items().contains(&self.id()) {
|
|
// TODO(emilio, #453): Figure out what to do when this happens
|
|
// legitimately, we could track the opaque stuff and disable the
|
|
// assertion there I guess.
|
|
warn!("Found non-allowlisted item in code generation: {:?}", self);
|
|
}
|
|
|
|
result.set_seen(self.id());
|
|
true
|
|
}
|
|
}
|
|
|
|
impl CodeGenerator for Item {
|
|
type Extra = ();
|
|
type Return = ();
|
|
|
|
fn codegen(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
result: &mut CodegenResult<'_>,
|
|
_extra: &(),
|
|
) {
|
|
debug!("<Item as CodeGenerator>::codegen: self = {:?}", self);
|
|
if !self.process_before_codegen(ctx, result) {
|
|
return;
|
|
}
|
|
|
|
match *self.kind() {
|
|
ItemKind::Module(ref module) => {
|
|
module.codegen(ctx, result, self);
|
|
}
|
|
ItemKind::Function(ref fun) => {
|
|
fun.codegen(ctx, result, self);
|
|
}
|
|
ItemKind::Var(ref var) => {
|
|
var.codegen(ctx, result, self);
|
|
}
|
|
ItemKind::Type(ref ty) => {
|
|
ty.codegen(ctx, result, self);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CodeGenerator for Module {
|
|
type Extra = Item;
|
|
type Return = ();
|
|
|
|
fn codegen(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
result: &mut CodegenResult<'_>,
|
|
item: &Item,
|
|
) {
|
|
debug!("<Module as CodeGenerator>::codegen: item = {:?}", item);
|
|
|
|
let codegen_self = |result: &mut CodegenResult,
|
|
found_any: &mut bool| {
|
|
for child in self.children() {
|
|
if ctx.codegen_items().contains(child) {
|
|
*found_any = true;
|
|
ctx.resolve_item(*child).codegen(ctx, result, &());
|
|
}
|
|
}
|
|
|
|
if item.id() == ctx.root_module() {
|
|
if result.saw_block {
|
|
utils::prepend_block_header(ctx, &mut *result);
|
|
}
|
|
if result.saw_bindgen_union {
|
|
utils::prepend_union_types(ctx, &mut *result);
|
|
}
|
|
if result.saw_incomplete_array {
|
|
utils::prepend_incomplete_array_types(ctx, &mut *result);
|
|
}
|
|
if ctx.need_bindgen_complex_type() {
|
|
utils::prepend_complex_type(&mut *result);
|
|
}
|
|
if result.saw_objc {
|
|
utils::prepend_objc_header(ctx, &mut *result);
|
|
}
|
|
if result.saw_bitfield_unit {
|
|
utils::prepend_bitfield_unit_type(ctx, &mut *result);
|
|
}
|
|
}
|
|
};
|
|
|
|
if !ctx.options().enable_cxx_namespaces ||
|
|
(self.is_inline() &&
|
|
!ctx.options().conservative_inline_namespaces)
|
|
{
|
|
codegen_self(result, &mut false);
|
|
return;
|
|
}
|
|
|
|
let mut found_any = false;
|
|
let inner_items = result.inner(|result| {
|
|
result.push(root_import(ctx, item));
|
|
|
|
let path = item
|
|
.namespace_aware_canonical_path(ctx)
|
|
.join("::")
|
|
.into_boxed_str();
|
|
if let Some(raw_lines) = ctx.options().module_lines.get(&path) {
|
|
for raw_line in raw_lines {
|
|
found_any = true;
|
|
result.push(
|
|
proc_macro2::TokenStream::from_str(raw_line).unwrap(),
|
|
);
|
|
}
|
|
}
|
|
|
|
codegen_self(result, &mut found_any);
|
|
});
|
|
|
|
// Don't bother creating an empty module.
|
|
if !found_any {
|
|
return;
|
|
}
|
|
|
|
let name = item.canonical_name(ctx);
|
|
let ident = ctx.rust_ident(name);
|
|
result.push(if item.id() == ctx.root_module() {
|
|
quote! {
|
|
#[allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
|
|
pub mod #ident {
|
|
#( #inner_items )*
|
|
}
|
|
}
|
|
} else {
|
|
quote! {
|
|
pub mod #ident {
|
|
#( #inner_items )*
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
impl CodeGenerator for Var {
|
|
type Extra = Item;
|
|
type Return = ();
|
|
|
|
fn codegen(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
result: &mut CodegenResult<'_>,
|
|
item: &Item,
|
|
) {
|
|
use crate::ir::var::VarType;
|
|
debug!("<Var as CodeGenerator>::codegen: item = {:?}", item);
|
|
debug_assert!(item.is_enabled_for_codegen(ctx));
|
|
|
|
let canonical_name = item.canonical_name(ctx);
|
|
|
|
if result.seen_var(&canonical_name) {
|
|
return;
|
|
}
|
|
result.saw_var(&canonical_name);
|
|
|
|
let canonical_ident = ctx.rust_ident(&canonical_name);
|
|
|
|
// We can't generate bindings to static variables of templates. The
|
|
// number of actual variables for a single declaration are open ended
|
|
// and we don't know what instantiations do or don't exist.
|
|
if !item.all_template_params(ctx).is_empty() {
|
|
return;
|
|
}
|
|
|
|
let mut attrs = vec![];
|
|
if let Some(comment) = item.comment(ctx) {
|
|
attrs.push(attributes::doc(comment));
|
|
}
|
|
|
|
let var_ty = self.ty();
|
|
let ty = var_ty.to_rust_ty_or_opaque(ctx, &());
|
|
|
|
if let Some(val) = self.val() {
|
|
match *val {
|
|
VarType::Bool(val) => {
|
|
result.push(quote! {
|
|
#(#attrs)*
|
|
pub const #canonical_ident : #ty = #val ;
|
|
});
|
|
}
|
|
VarType::Int(val) => {
|
|
let int_kind = var_ty
|
|
.into_resolver()
|
|
.through_type_aliases()
|
|
.through_type_refs()
|
|
.resolve(ctx)
|
|
.expect_type()
|
|
.as_integer()
|
|
.unwrap();
|
|
let val = if int_kind.is_signed() {
|
|
helpers::ast_ty::int_expr(val)
|
|
} else {
|
|
helpers::ast_ty::uint_expr(val as _)
|
|
};
|
|
result.push(quote! {
|
|
#(#attrs)*
|
|
pub const #canonical_ident : #ty = #val ;
|
|
});
|
|
}
|
|
VarType::String(ref bytes) => {
|
|
let prefix = ctx.trait_prefix();
|
|
|
|
let options = ctx.options();
|
|
let rust_features = options.rust_features;
|
|
|
|
let mut cstr_bytes = bytes.clone();
|
|
cstr_bytes.push(0);
|
|
let len = proc_macro2::Literal::usize_unsuffixed(
|
|
cstr_bytes.len(),
|
|
);
|
|
|
|
// TODO: Here we ignore the type we just made up, probably
|
|
// we should refactor how the variable type and ty ID work.
|
|
let array_ty = quote! { [u8; #len] };
|
|
let cstr_ty = quote! { ::#prefix::ffi::CStr };
|
|
|
|
let bytes = proc_macro2::Literal::byte_string(&cstr_bytes);
|
|
|
|
if options.generate_cstr &&
|
|
rust_features.const_cstr &&
|
|
CStr::from_bytes_with_nul(&cstr_bytes).is_ok()
|
|
{
|
|
result.push(quote! {
|
|
#(#attrs)*
|
|
#[allow(unsafe_code)]
|
|
pub const #canonical_ident: &#cstr_ty = unsafe {
|
|
#cstr_ty::from_bytes_with_nul_unchecked(#bytes)
|
|
};
|
|
});
|
|
} else {
|
|
let lifetime = if rust_features.static_lifetime_elision
|
|
{
|
|
None
|
|
} else {
|
|
Some(quote! { 'static })
|
|
}
|
|
.into_iter();
|
|
|
|
result.push(quote! {
|
|
#(#attrs)*
|
|
pub const #canonical_ident: &#(#lifetime )*#array_ty = #bytes ;
|
|
});
|
|
}
|
|
}
|
|
VarType::Float(f) => {
|
|
if let Ok(expr) = helpers::ast_ty::float_expr(ctx, f) {
|
|
result.push(quote! {
|
|
#(#attrs)*
|
|
pub const #canonical_ident : #ty = #expr ;
|
|
});
|
|
}
|
|
}
|
|
VarType::Char(c) => {
|
|
result.push(quote! {
|
|
#(#attrs)*
|
|
pub const #canonical_ident : #ty = #c ;
|
|
});
|
|
}
|
|
}
|
|
} else {
|
|
// If necessary, apply a `#[link_name]` attribute
|
|
if let Some(link_name) = self.link_name() {
|
|
attrs.push(attributes::link_name::<false>(link_name));
|
|
} else {
|
|
let link_name =
|
|
self.mangled_name().unwrap_or_else(|| self.name());
|
|
if !utils::names_will_be_identical_after_mangling(
|
|
&canonical_name,
|
|
link_name,
|
|
None,
|
|
) {
|
|
attrs.push(attributes::link_name::<false>(link_name));
|
|
}
|
|
}
|
|
|
|
let maybe_mut = if self.is_const() {
|
|
quote! {}
|
|
} else {
|
|
quote! { mut }
|
|
};
|
|
|
|
let tokens = quote!(
|
|
extern "C" {
|
|
#(#attrs)*
|
|
pub static #maybe_mut #canonical_ident: #ty;
|
|
}
|
|
);
|
|
|
|
result.push(tokens);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CodeGenerator for Type {
|
|
type Extra = Item;
|
|
type Return = ();
|
|
|
|
fn codegen(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
result: &mut CodegenResult<'_>,
|
|
item: &Item,
|
|
) {
|
|
debug!("<Type as CodeGenerator>::codegen: item = {:?}", item);
|
|
debug_assert!(item.is_enabled_for_codegen(ctx));
|
|
|
|
match *self.kind() {
|
|
TypeKind::Void |
|
|
TypeKind::NullPtr |
|
|
TypeKind::Int(..) |
|
|
TypeKind::Float(..) |
|
|
TypeKind::Complex(..) |
|
|
TypeKind::Array(..) |
|
|
TypeKind::Vector(..) |
|
|
TypeKind::Pointer(..) |
|
|
TypeKind::Reference(..) |
|
|
TypeKind::Function(..) |
|
|
TypeKind::ResolvedTypeRef(..) |
|
|
TypeKind::Opaque |
|
|
TypeKind::TypeParam => {
|
|
// These items don't need code generation, they only need to be
|
|
// converted to rust types in fields, arguments, and such.
|
|
// NOTE(emilio): If you add to this list, make sure to also add
|
|
// it to BindgenContext::compute_allowlisted_and_codegen_items.
|
|
}
|
|
TypeKind::TemplateInstantiation(ref inst) => {
|
|
inst.codegen(ctx, result, item)
|
|
}
|
|
TypeKind::BlockPointer(inner) => {
|
|
if !ctx.options().generate_block {
|
|
return;
|
|
}
|
|
|
|
let inner_item =
|
|
inner.into_resolver().through_type_refs().resolve(ctx);
|
|
let name = item.canonical_name(ctx);
|
|
|
|
let inner_rust_type = {
|
|
if let TypeKind::Function(fnsig) =
|
|
inner_item.kind().expect_type().kind()
|
|
{
|
|
utils::fnsig_block(ctx, fnsig)
|
|
} else {
|
|
panic!("invalid block typedef: {:?}", inner_item)
|
|
}
|
|
};
|
|
|
|
let rust_name = ctx.rust_ident(name);
|
|
|
|
let mut tokens = if let Some(comment) = item.comment(ctx) {
|
|
attributes::doc(comment)
|
|
} else {
|
|
quote! {}
|
|
};
|
|
|
|
tokens.append_all(quote! {
|
|
pub type #rust_name = #inner_rust_type ;
|
|
});
|
|
|
|
result.push(tokens);
|
|
result.saw_block();
|
|
}
|
|
TypeKind::Comp(ref ci) => ci.codegen(ctx, result, item),
|
|
TypeKind::TemplateAlias(inner, _) | TypeKind::Alias(inner) => {
|
|
let inner_item =
|
|
inner.into_resolver().through_type_refs().resolve(ctx);
|
|
let name = item.canonical_name(ctx);
|
|
let path = item.canonical_path(ctx);
|
|
|
|
{
|
|
let through_type_aliases = inner
|
|
.into_resolver()
|
|
.through_type_refs()
|
|
.through_type_aliases()
|
|
.resolve(ctx);
|
|
|
|
// Try to catch the common pattern:
|
|
//
|
|
// typedef struct foo { ... } foo;
|
|
//
|
|
// here, and also other more complex cases like #946.
|
|
if through_type_aliases.canonical_path(ctx) == path {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If this is a known named type, disallow generating anything
|
|
// for it too. If size_t -> usize conversions are enabled, we
|
|
// need to check that these conversions are permissible, but
|
|
// nothing needs to be generated, still.
|
|
let spelling = self.name().expect("Unnamed alias?");
|
|
if utils::type_from_named(ctx, spelling).is_some() {
|
|
if let "size_t" | "ssize_t" = spelling {
|
|
let layout = inner_item
|
|
.kind()
|
|
.expect_type()
|
|
.layout(ctx)
|
|
.expect("No layout?");
|
|
assert_eq!(
|
|
layout.size,
|
|
ctx.target_pointer_size(),
|
|
"Target platform requires `--no-size_t-is-usize`. The size of `{}` ({}) does not match the target pointer size ({})",
|
|
spelling,
|
|
layout.size,
|
|
ctx.target_pointer_size(),
|
|
);
|
|
assert_eq!(
|
|
layout.align,
|
|
ctx.target_pointer_size(),
|
|
"Target platform requires `--no-size_t-is-usize`. The alignment of `{}` ({}) does not match the target pointer size ({})",
|
|
spelling,
|
|
layout.align,
|
|
ctx.target_pointer_size(),
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
|
|
let mut outer_params = item.used_template_params(ctx);
|
|
|
|
let is_opaque = item.is_opaque(ctx, &());
|
|
let inner_rust_type = if is_opaque {
|
|
outer_params = vec![];
|
|
self.to_opaque(ctx, item)
|
|
} else {
|
|
// Its possible that we have better layout information than
|
|
// the inner type does, so fall back to an opaque blob based
|
|
// on our layout if converting the inner item fails.
|
|
inner_item
|
|
.try_to_rust_ty_or_opaque(ctx, &())
|
|
.unwrap_or_else(|_| self.to_opaque(ctx, item))
|
|
.with_implicit_template_params(ctx, inner_item)
|
|
};
|
|
|
|
{
|
|
// FIXME(emilio): This is a workaround to avoid generating
|
|
// incorrect type aliases because of types that we haven't
|
|
// been able to resolve (because, eg, they depend on a
|
|
// template parameter).
|
|
//
|
|
// It's kind of a shame not generating them even when they
|
|
// could be referenced, but we already do the same for items
|
|
// with invalid template parameters, and at least this way
|
|
// they can be replaced, instead of generating plain invalid
|
|
// code.
|
|
let inner_canon_type =
|
|
inner_item.expect_type().canonical_type(ctx);
|
|
if inner_canon_type.is_invalid_type_param() {
|
|
warn!(
|
|
"Item contained invalid named type, skipping: \
|
|
{:?}, {:?}",
|
|
item, inner_item
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
let rust_name = ctx.rust_ident(&name);
|
|
|
|
let mut tokens = if let Some(comment) = item.comment(ctx) {
|
|
attributes::doc(comment)
|
|
} else {
|
|
quote! {}
|
|
};
|
|
|
|
let alias_style = if ctx.options().type_alias.matches(&name) {
|
|
AliasVariation::TypeAlias
|
|
} else if ctx.options().new_type_alias.matches(&name) {
|
|
AliasVariation::NewType
|
|
} else if ctx.options().new_type_alias_deref.matches(&name) {
|
|
AliasVariation::NewTypeDeref
|
|
} else {
|
|
ctx.options().default_alias_style
|
|
};
|
|
|
|
// We prefer using `pub use` over `pub type` because of:
|
|
// https://github.com/rust-lang/rust/issues/26264
|
|
if matches!(inner_rust_type, syn::Type::Path(_)) &&
|
|
outer_params.is_empty() &&
|
|
!is_opaque &&
|
|
alias_style == AliasVariation::TypeAlias &&
|
|
inner_item.expect_type().canonical_type(ctx).is_enum()
|
|
{
|
|
tokens.append_all(quote! {
|
|
pub use
|
|
});
|
|
let path = top_level_path(ctx, item);
|
|
tokens.append_separated(path, quote!(::));
|
|
tokens.append_all(quote! {
|
|
:: #inner_rust_type as #rust_name ;
|
|
});
|
|
result.push(tokens);
|
|
return;
|
|
}
|
|
|
|
tokens.append_all(match alias_style {
|
|
AliasVariation::TypeAlias => quote! {
|
|
pub type #rust_name
|
|
},
|
|
AliasVariation::NewType | AliasVariation::NewTypeDeref => {
|
|
assert!(
|
|
ctx.options().rust_features().repr_transparent,
|
|
"repr_transparent feature is required to use {:?}",
|
|
alias_style
|
|
);
|
|
|
|
let mut attributes =
|
|
vec![attributes::repr("transparent")];
|
|
let packed = false; // Types can't be packed in Rust.
|
|
let derivable_traits =
|
|
derives_of_item(item, ctx, packed);
|
|
if !derivable_traits.is_empty() {
|
|
let derives: Vec<_> = derivable_traits.into();
|
|
attributes.push(attributes::derives(&derives))
|
|
}
|
|
|
|
quote! {
|
|
#( #attributes )*
|
|
pub struct #rust_name
|
|
}
|
|
}
|
|
});
|
|
|
|
let params: Vec<_> = outer_params
|
|
.into_iter()
|
|
.filter_map(|p| p.as_template_param(ctx, &()))
|
|
.collect();
|
|
if params
|
|
.iter()
|
|
.any(|p| ctx.resolve_type(*p).is_invalid_type_param())
|
|
{
|
|
warn!(
|
|
"Item contained invalid template \
|
|
parameter: {:?}",
|
|
item
|
|
);
|
|
return;
|
|
}
|
|
let params: Vec<_> = params
|
|
.iter()
|
|
.map(|p| {
|
|
p.try_to_rust_ty(ctx, &()).expect(
|
|
"type parameters can always convert to rust ty OK",
|
|
)
|
|
})
|
|
.collect();
|
|
|
|
if !params.is_empty() {
|
|
tokens.append_all(quote! {
|
|
< #( #params ),* >
|
|
});
|
|
}
|
|
|
|
let access_spec =
|
|
access_specifier(ctx.options().default_visibility);
|
|
tokens.append_all(match alias_style {
|
|
AliasVariation::TypeAlias => quote! {
|
|
= #inner_rust_type ;
|
|
},
|
|
AliasVariation::NewType | AliasVariation::NewTypeDeref => {
|
|
quote! {
|
|
(#access_spec #inner_rust_type) ;
|
|
}
|
|
}
|
|
});
|
|
|
|
if alias_style == AliasVariation::NewTypeDeref {
|
|
let prefix = ctx.trait_prefix();
|
|
tokens.append_all(quote! {
|
|
impl ::#prefix::ops::Deref for #rust_name {
|
|
type Target = #inner_rust_type;
|
|
#[inline]
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
impl ::#prefix::ops::DerefMut for #rust_name {
|
|
#[inline]
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.0
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
result.push(tokens);
|
|
}
|
|
TypeKind::Enum(ref ei) => ei.codegen(ctx, result, item),
|
|
TypeKind::ObjCId | TypeKind::ObjCSel => {
|
|
result.saw_objc();
|
|
}
|
|
TypeKind::ObjCInterface(ref interface) => {
|
|
interface.codegen(ctx, result, item)
|
|
}
|
|
ref u @ TypeKind::UnresolvedTypeRef(..) => {
|
|
unreachable!("Should have been resolved after parsing {:?}!", u)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Vtable<'a> {
|
|
item_id: ItemId,
|
|
/// A reference to the originating compound object.
|
|
#[allow(dead_code)]
|
|
comp_info: &'a CompInfo,
|
|
}
|
|
|
|
impl<'a> Vtable<'a> {
|
|
fn new(item_id: ItemId, comp_info: &'a CompInfo) -> Self {
|
|
Vtable { item_id, comp_info }
|
|
}
|
|
}
|
|
|
|
impl<'a> CodeGenerator for Vtable<'a> {
|
|
type Extra = Item;
|
|
type Return = ();
|
|
|
|
fn codegen(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
result: &mut CodegenResult<'_>,
|
|
item: &Item,
|
|
) {
|
|
assert_eq!(item.id(), self.item_id);
|
|
debug_assert!(item.is_enabled_for_codegen(ctx));
|
|
let name = ctx.rust_ident(self.canonical_name(ctx));
|
|
|
|
// For now, we will only generate vtables for classes that:
|
|
// - do not inherit from others (compilers merge VTable from primary parent class).
|
|
// - do not contain a virtual destructor (requires ordering; platforms generate different vtables).
|
|
if ctx.options().vtable_generation &&
|
|
self.comp_info.base_members().is_empty() &&
|
|
self.comp_info.destructor().is_none()
|
|
{
|
|
let class_ident = ctx.rust_ident(self.item_id.canonical_name(ctx));
|
|
|
|
let methods = self
|
|
.comp_info
|
|
.methods()
|
|
.iter()
|
|
.filter_map(|m| {
|
|
if !m.is_virtual() {
|
|
return None;
|
|
}
|
|
|
|
let function_item = ctx.resolve_item(m.signature());
|
|
let function = function_item.expect_function();
|
|
let signature_item = ctx.resolve_item(function.signature());
|
|
let signature = match signature_item.expect_type().kind() {
|
|
TypeKind::Function(ref sig) => sig,
|
|
_ => panic!("Function signature type mismatch"),
|
|
};
|
|
|
|
// FIXME: Is there a canonical name without the class prepended?
|
|
let function_name = function_item.canonical_name(ctx);
|
|
|
|
// FIXME: Need to account for overloading with times_seen (separately from regular function path).
|
|
let function_name = ctx.rust_ident(function_name);
|
|
let mut args = utils::fnsig_arguments(ctx, signature);
|
|
let ret = utils::fnsig_return_ty(ctx, signature);
|
|
|
|
args[0] = if m.is_const() {
|
|
quote! { this: *const #class_ident }
|
|
} else {
|
|
quote! { this: *mut #class_ident }
|
|
};
|
|
|
|
Some(quote! {
|
|
pub #function_name : unsafe extern "C" fn( #( #args ),* ) #ret
|
|
})
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
result.push(quote! {
|
|
#[repr(C)]
|
|
pub struct #name {
|
|
#( #methods ),*
|
|
}
|
|
})
|
|
} else {
|
|
// For the cases we don't support, simply generate an empty struct.
|
|
let void = helpers::ast_ty::c_void(ctx);
|
|
|
|
result.push(quote! {
|
|
#[repr(C)]
|
|
pub struct #name ( #void );
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> ItemCanonicalName for Vtable<'a> {
|
|
fn canonical_name(&self, ctx: &BindgenContext) -> String {
|
|
format!("{}__bindgen_vtable", self.item_id.canonical_name(ctx))
|
|
}
|
|
}
|
|
|
|
impl<'a> TryToRustTy for Vtable<'a> {
|
|
type Extra = ();
|
|
|
|
fn try_to_rust_ty(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
_: &(),
|
|
) -> error::Result<syn::Type> {
|
|
let name = ctx.rust_ident(self.canonical_name(ctx));
|
|
Ok(syn::parse_quote! { #name })
|
|
}
|
|
}
|
|
|
|
impl CodeGenerator for TemplateInstantiation {
|
|
type Extra = Item;
|
|
type Return = ();
|
|
|
|
fn codegen(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
result: &mut CodegenResult<'_>,
|
|
item: &Item,
|
|
) {
|
|
debug_assert!(item.is_enabled_for_codegen(ctx));
|
|
|
|
// Although uses of instantiations don't need code generation, and are
|
|
// just converted to rust types in fields, vars, etc, we take this
|
|
// opportunity to generate tests for their layout here. If the
|
|
// instantiation is opaque, then its presumably because we don't
|
|
// properly understand it (maybe because of specializations), and so we
|
|
// shouldn't emit layout tests either.
|
|
if !ctx.options().layout_tests || self.is_opaque(ctx, item) {
|
|
return;
|
|
}
|
|
|
|
// If there are any unbound type parameters, then we can't generate a
|
|
// layout test because we aren't dealing with a concrete type with a
|
|
// concrete size and alignment.
|
|
if ctx.uses_any_template_parameters(item.id()) {
|
|
return;
|
|
}
|
|
|
|
let layout = item.kind().expect_type().layout(ctx);
|
|
|
|
if let Some(layout) = layout {
|
|
let size = layout.size;
|
|
let align = layout.align;
|
|
|
|
let name = item.full_disambiguated_name(ctx);
|
|
let mut fn_name =
|
|
format!("__bindgen_test_layout_{}_instantiation", name);
|
|
let times_seen = result.overload_number(&fn_name);
|
|
if times_seen > 0 {
|
|
write!(&mut fn_name, "_{}", times_seen).unwrap();
|
|
}
|
|
|
|
let fn_name = ctx.rust_ident_raw(fn_name);
|
|
|
|
let prefix = ctx.trait_prefix();
|
|
let ident = item.to_rust_ty_or_opaque(ctx, &());
|
|
let size_of_expr = quote! {
|
|
::#prefix::mem::size_of::<#ident>()
|
|
};
|
|
let align_of_expr = quote! {
|
|
::#prefix::mem::align_of::<#ident>()
|
|
};
|
|
|
|
let item = quote! {
|
|
#[test]
|
|
fn #fn_name() {
|
|
assert_eq!(#size_of_expr, #size,
|
|
concat!("Size of template specialization: ",
|
|
stringify!(#ident)));
|
|
assert_eq!(#align_of_expr, #align,
|
|
concat!("Alignment of template specialization: ",
|
|
stringify!(#ident)));
|
|
}
|
|
};
|
|
|
|
result.push(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Trait for implementing the code generation of a struct or union field.
|
|
trait FieldCodegen<'a> {
|
|
type Extra;
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
fn codegen<F, M>(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
visibility_kind: FieldVisibilityKind,
|
|
accessor_kind: FieldAccessorKind,
|
|
parent: &CompInfo,
|
|
parent_item: &Item,
|
|
result: &mut CodegenResult,
|
|
struct_layout: &mut StructLayoutTracker,
|
|
fields: &mut F,
|
|
methods: &mut M,
|
|
extra: Self::Extra,
|
|
) where
|
|
F: Extend<proc_macro2::TokenStream>,
|
|
M: Extend<proc_macro2::TokenStream>;
|
|
}
|
|
|
|
impl<'a> FieldCodegen<'a> for Field {
|
|
type Extra = ();
|
|
|
|
fn codegen<F, M>(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
visibility_kind: FieldVisibilityKind,
|
|
accessor_kind: FieldAccessorKind,
|
|
parent: &CompInfo,
|
|
parent_item: &Item,
|
|
result: &mut CodegenResult,
|
|
struct_layout: &mut StructLayoutTracker,
|
|
fields: &mut F,
|
|
methods: &mut M,
|
|
_: (),
|
|
) where
|
|
F: Extend<proc_macro2::TokenStream>,
|
|
M: Extend<proc_macro2::TokenStream>,
|
|
{
|
|
match *self {
|
|
Field::DataMember(ref data) => {
|
|
data.codegen(
|
|
ctx,
|
|
visibility_kind,
|
|
accessor_kind,
|
|
parent,
|
|
parent_item,
|
|
result,
|
|
struct_layout,
|
|
fields,
|
|
methods,
|
|
(),
|
|
);
|
|
}
|
|
Field::Bitfields(ref unit) => {
|
|
unit.codegen(
|
|
ctx,
|
|
visibility_kind,
|
|
accessor_kind,
|
|
parent,
|
|
parent_item,
|
|
result,
|
|
struct_layout,
|
|
fields,
|
|
methods,
|
|
(),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn wrap_union_field_if_needed(
|
|
ctx: &BindgenContext,
|
|
struct_layout: &StructLayoutTracker,
|
|
ty: syn::Type,
|
|
result: &mut CodegenResult,
|
|
) -> syn::Type {
|
|
if struct_layout.is_rust_union() {
|
|
if struct_layout.can_copy_union_fields() {
|
|
ty
|
|
} else {
|
|
let prefix = ctx.trait_prefix();
|
|
syn::parse_quote! { ::#prefix::mem::ManuallyDrop<#ty> }
|
|
}
|
|
} else {
|
|
result.saw_bindgen_union();
|
|
if ctx.options().enable_cxx_namespaces {
|
|
syn::parse_quote! { root::__BindgenUnionField<#ty> }
|
|
} else {
|
|
syn::parse_quote! { __BindgenUnionField<#ty> }
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> FieldCodegen<'a> for FieldData {
|
|
type Extra = ();
|
|
|
|
fn codegen<F, M>(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
parent_visibility_kind: FieldVisibilityKind,
|
|
accessor_kind: FieldAccessorKind,
|
|
parent: &CompInfo,
|
|
parent_item: &Item,
|
|
result: &mut CodegenResult,
|
|
struct_layout: &mut StructLayoutTracker,
|
|
fields: &mut F,
|
|
methods: &mut M,
|
|
_: (),
|
|
) where
|
|
F: Extend<proc_macro2::TokenStream>,
|
|
M: Extend<proc_macro2::TokenStream>,
|
|
{
|
|
// Bitfields are handled by `FieldCodegen` implementations for
|
|
// `BitfieldUnit` and `Bitfield`.
|
|
assert!(self.bitfield_width().is_none());
|
|
|
|
let field_item =
|
|
self.ty().into_resolver().through_type_refs().resolve(ctx);
|
|
let field_ty = field_item.expect_type();
|
|
let ty = self
|
|
.ty()
|
|
.to_rust_ty_or_opaque(ctx, &())
|
|
.with_implicit_template_params(ctx, field_item);
|
|
|
|
// NB: If supported, we use proper `union` types.
|
|
let ty = if parent.is_union() {
|
|
wrap_union_field_if_needed(ctx, struct_layout, ty, result)
|
|
} else if let Some(item) = field_ty.is_incomplete_array(ctx) {
|
|
result.saw_incomplete_array();
|
|
|
|
let inner = item.to_rust_ty_or_opaque(ctx, &());
|
|
|
|
if ctx.options().enable_cxx_namespaces {
|
|
syn::parse_quote! { root::__IncompleteArrayField<#inner> }
|
|
} else {
|
|
syn::parse_quote! { __IncompleteArrayField<#inner> }
|
|
}
|
|
} else {
|
|
ty
|
|
};
|
|
|
|
let mut field = quote! {};
|
|
if ctx.options().generate_comments {
|
|
if let Some(raw_comment) = self.comment() {
|
|
let comment = ctx.options().process_comment(raw_comment);
|
|
field = attributes::doc(comment);
|
|
}
|
|
}
|
|
|
|
let field_name = self
|
|
.name()
|
|
.map(|name| ctx.rust_mangle(name).into_owned())
|
|
.expect("Each field should have a name in codegen!");
|
|
let field_name = field_name.as_str();
|
|
let field_ident = ctx.rust_ident_raw(field_name);
|
|
|
|
if let Some(padding_field) =
|
|
struct_layout.saw_field(field_name, field_ty, self.offset())
|
|
{
|
|
fields.extend(Some(padding_field));
|
|
}
|
|
|
|
let visibility = compute_visibility(
|
|
ctx,
|
|
self.is_public(),
|
|
ctx.options().last_callback(|cb| {
|
|
cb.field_visibility(FieldInfo {
|
|
type_name: &parent_item.canonical_name(ctx),
|
|
field_name,
|
|
})
|
|
}),
|
|
self.annotations(),
|
|
parent_visibility_kind,
|
|
);
|
|
let accessor_kind =
|
|
self.annotations().accessor_kind().unwrap_or(accessor_kind);
|
|
|
|
match visibility {
|
|
FieldVisibilityKind::Private => {
|
|
field.append_all(quote! {
|
|
#field_ident : #ty ,
|
|
});
|
|
}
|
|
FieldVisibilityKind::PublicCrate => {
|
|
field.append_all(quote! {
|
|
pub(crate) #field_ident : #ty ,
|
|
});
|
|
}
|
|
FieldVisibilityKind::Public => {
|
|
field.append_all(quote! {
|
|
pub #field_ident : #ty ,
|
|
});
|
|
}
|
|
}
|
|
|
|
fields.extend(Some(field));
|
|
|
|
// TODO: Factor the following code out, please!
|
|
if accessor_kind == FieldAccessorKind::None {
|
|
return;
|
|
}
|
|
|
|
let getter_name = ctx.rust_ident_raw(format!("get_{}", field_name));
|
|
let mutable_getter_name =
|
|
ctx.rust_ident_raw(format!("get_{}_mut", field_name));
|
|
|
|
methods.extend(Some(match accessor_kind {
|
|
FieldAccessorKind::None => unreachable!(),
|
|
FieldAccessorKind::Regular => {
|
|
quote! {
|
|
#[inline]
|
|
pub fn #getter_name(&self) -> & #ty {
|
|
&self.#field_ident
|
|
}
|
|
|
|
#[inline]
|
|
pub fn #mutable_getter_name(&mut self) -> &mut #ty {
|
|
&mut self.#field_ident
|
|
}
|
|
}
|
|
}
|
|
FieldAccessorKind::Unsafe => {
|
|
quote! {
|
|
#[inline]
|
|
pub unsafe fn #getter_name(&self) -> & #ty {
|
|
&self.#field_ident
|
|
}
|
|
|
|
#[inline]
|
|
pub unsafe fn #mutable_getter_name(&mut self) -> &mut #ty {
|
|
&mut self.#field_ident
|
|
}
|
|
}
|
|
}
|
|
FieldAccessorKind::Immutable => {
|
|
quote! {
|
|
#[inline]
|
|
pub fn #getter_name(&self) -> & #ty {
|
|
&self.#field_ident
|
|
}
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
|
|
impl BitfieldUnit {
|
|
/// Get the constructor name for this bitfield unit.
|
|
fn ctor_name(&self) -> proc_macro2::TokenStream {
|
|
let ctor_name = Ident::new(
|
|
&format!("new_bitfield_{}", self.nth()),
|
|
Span::call_site(),
|
|
);
|
|
quote! {
|
|
#ctor_name
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Bitfield {
|
|
/// Extend an under construction bitfield unit constructor with this
|
|
/// bitfield. This sets the relevant bits on the `__bindgen_bitfield_unit`
|
|
/// variable that's being constructed.
|
|
fn extend_ctor_impl(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
param_name: proc_macro2::TokenStream,
|
|
mut ctor_impl: proc_macro2::TokenStream,
|
|
) -> proc_macro2::TokenStream {
|
|
let bitfield_ty = ctx.resolve_type(self.ty());
|
|
let bitfield_ty_layout = bitfield_ty
|
|
.layout(ctx)
|
|
.expect("Bitfield without layout? Gah!");
|
|
let bitfield_int_ty = helpers::integer_type(ctx, bitfield_ty_layout)
|
|
.expect(
|
|
"Should already have verified that the bitfield is \
|
|
representable as an int",
|
|
);
|
|
|
|
let offset = self.offset_into_unit();
|
|
let width = self.width() as u8;
|
|
let prefix = ctx.trait_prefix();
|
|
|
|
ctor_impl.append_all(quote! {
|
|
__bindgen_bitfield_unit.set(
|
|
#offset,
|
|
#width,
|
|
{
|
|
let #param_name: #bitfield_int_ty = unsafe {
|
|
::#prefix::mem::transmute(#param_name)
|
|
};
|
|
#param_name as u64
|
|
}
|
|
);
|
|
});
|
|
|
|
ctor_impl
|
|
}
|
|
}
|
|
|
|
fn access_specifier(
|
|
visibility: FieldVisibilityKind,
|
|
) -> proc_macro2::TokenStream {
|
|
match visibility {
|
|
FieldVisibilityKind::Private => quote! {},
|
|
FieldVisibilityKind::PublicCrate => quote! { pub(crate) },
|
|
FieldVisibilityKind::Public => quote! { pub },
|
|
}
|
|
}
|
|
|
|
/// Compute a fields or structs visibility based on multiple conditions.
|
|
/// 1. If the element was declared public, and we respect such CXX accesses specs
|
|
/// (context option) => By default Public, but this can be overruled by an `annotation`.
|
|
///
|
|
/// 2. If the element was declared private, and we respect such CXX accesses specs
|
|
/// (context option) => By default Private, but this can be overruled by an `annotation`.
|
|
///
|
|
/// 3. If we do not respect visibility modifiers, the result depends on the `annotation`,
|
|
/// if any, or the passed `default_kind`.
|
|
///
|
|
fn compute_visibility(
|
|
ctx: &BindgenContext,
|
|
is_declared_public: bool,
|
|
callback_override: Option<FieldVisibilityKind>,
|
|
annotations: &Annotations,
|
|
default_kind: FieldVisibilityKind,
|
|
) -> FieldVisibilityKind {
|
|
callback_override
|
|
.or_else(|| annotations.visibility_kind())
|
|
.unwrap_or_else(|| {
|
|
match (is_declared_public, ctx.options().respect_cxx_access_specs) {
|
|
(true, true) => {
|
|
// declared as public, cxx specs are respected
|
|
FieldVisibilityKind::Public
|
|
}
|
|
(false, true) => {
|
|
// declared as private, cxx specs are respected
|
|
FieldVisibilityKind::Private
|
|
}
|
|
(_, false) => {
|
|
// cxx specs are not respected, declaration does not matter.
|
|
default_kind
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
impl<'a> FieldCodegen<'a> for BitfieldUnit {
|
|
type Extra = ();
|
|
|
|
fn codegen<F, M>(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
visibility_kind: FieldVisibilityKind,
|
|
accessor_kind: FieldAccessorKind,
|
|
parent: &CompInfo,
|
|
parent_item: &Item,
|
|
result: &mut CodegenResult,
|
|
struct_layout: &mut StructLayoutTracker,
|
|
fields: &mut F,
|
|
methods: &mut M,
|
|
_: (),
|
|
) where
|
|
F: Extend<proc_macro2::TokenStream>,
|
|
M: Extend<proc_macro2::TokenStream>,
|
|
{
|
|
use crate::ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT;
|
|
|
|
result.saw_bitfield_unit();
|
|
|
|
let layout = self.layout();
|
|
let unit_field_ty = helpers::bitfield_unit(ctx, layout);
|
|
let field_ty = {
|
|
let unit_field_ty = unit_field_ty.clone();
|
|
if parent.is_union() {
|
|
wrap_union_field_if_needed(
|
|
ctx,
|
|
struct_layout,
|
|
unit_field_ty,
|
|
result,
|
|
)
|
|
} else {
|
|
unit_field_ty
|
|
}
|
|
};
|
|
|
|
{
|
|
let align_field_name = format!("_bitfield_align_{}", self.nth());
|
|
let align_field_ident = ctx.rust_ident(align_field_name);
|
|
let align_ty = match self.layout().align {
|
|
n if n >= 8 => quote! { u64 },
|
|
4 => quote! { u32 },
|
|
2 => quote! { u16 },
|
|
_ => quote! { u8 },
|
|
};
|
|
let access_spec = access_specifier(visibility_kind);
|
|
let align_field = quote! {
|
|
#access_spec #align_field_ident: [#align_ty; 0],
|
|
};
|
|
fields.extend(Some(align_field));
|
|
}
|
|
|
|
let unit_field_name = format!("_bitfield_{}", self.nth());
|
|
let unit_field_ident = ctx.rust_ident(&unit_field_name);
|
|
|
|
let ctor_name = self.ctor_name();
|
|
let mut ctor_params = vec![];
|
|
let mut ctor_impl = quote! {};
|
|
|
|
// We cannot generate any constructor if the underlying storage can't
|
|
// implement AsRef<[u8]> / AsMut<[u8]> / etc, or can't derive Default.
|
|
//
|
|
// We don't check `larger_arrays` here because Default does still have
|
|
// the 32 items limitation.
|
|
let mut generate_ctor = layout.size <= RUST_DERIVE_IN_ARRAY_LIMIT;
|
|
|
|
let mut unit_visibility = visibility_kind;
|
|
for bf in self.bitfields() {
|
|
// Codegen not allowed for anonymous bitfields
|
|
if bf.name().is_none() {
|
|
continue;
|
|
}
|
|
|
|
if layout.size > RUST_DERIVE_IN_ARRAY_LIMIT &&
|
|
!ctx.options().rust_features().larger_arrays
|
|
{
|
|
continue;
|
|
}
|
|
|
|
let mut bitfield_representable_as_int = true;
|
|
let mut bitfield_visibility = visibility_kind;
|
|
bf.codegen(
|
|
ctx,
|
|
visibility_kind,
|
|
accessor_kind,
|
|
parent,
|
|
parent_item,
|
|
result,
|
|
struct_layout,
|
|
fields,
|
|
methods,
|
|
(
|
|
&unit_field_name,
|
|
&mut bitfield_representable_as_int,
|
|
&mut bitfield_visibility,
|
|
),
|
|
);
|
|
if bitfield_visibility < unit_visibility {
|
|
unit_visibility = bitfield_visibility;
|
|
}
|
|
|
|
// Generating a constructor requires the bitfield to be representable as an integer.
|
|
if !bitfield_representable_as_int {
|
|
generate_ctor = false;
|
|
continue;
|
|
}
|
|
|
|
let param_name = bitfield_getter_name(ctx, bf);
|
|
let bitfield_ty_item = ctx.resolve_item(bf.ty());
|
|
let bitfield_ty = bitfield_ty_item.expect_type();
|
|
let bitfield_ty =
|
|
bitfield_ty.to_rust_ty_or_opaque(ctx, bitfield_ty_item);
|
|
|
|
ctor_params.push(quote! {
|
|
#param_name : #bitfield_ty
|
|
});
|
|
ctor_impl = bf.extend_ctor_impl(ctx, param_name, ctor_impl);
|
|
}
|
|
|
|
let access_spec = access_specifier(unit_visibility);
|
|
|
|
let field = quote! {
|
|
#access_spec #unit_field_ident : #field_ty ,
|
|
};
|
|
fields.extend(Some(field));
|
|
|
|
if generate_ctor {
|
|
methods.extend(Some(quote! {
|
|
#[inline]
|
|
#access_spec fn #ctor_name ( #( #ctor_params ),* ) -> #unit_field_ty {
|
|
let mut __bindgen_bitfield_unit: #unit_field_ty = Default::default();
|
|
#ctor_impl
|
|
__bindgen_bitfield_unit
|
|
}
|
|
}));
|
|
}
|
|
|
|
struct_layout.saw_bitfield_unit(layout);
|
|
}
|
|
}
|
|
|
|
fn bitfield_getter_name(
|
|
ctx: &BindgenContext,
|
|
bitfield: &Bitfield,
|
|
) -> proc_macro2::TokenStream {
|
|
let name = bitfield.getter_name();
|
|
let name = ctx.rust_ident_raw(name);
|
|
quote! { #name }
|
|
}
|
|
|
|
fn bitfield_setter_name(
|
|
ctx: &BindgenContext,
|
|
bitfield: &Bitfield,
|
|
) -> proc_macro2::TokenStream {
|
|
let setter = bitfield.setter_name();
|
|
let setter = ctx.rust_ident_raw(setter);
|
|
quote! { #setter }
|
|
}
|
|
|
|
impl<'a> FieldCodegen<'a> for Bitfield {
|
|
type Extra = (&'a str, &'a mut bool, &'a mut FieldVisibilityKind);
|
|
|
|
fn codegen<F, M>(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
visibility_kind: FieldVisibilityKind,
|
|
_accessor_kind: FieldAccessorKind,
|
|
parent: &CompInfo,
|
|
parent_item: &Item,
|
|
_result: &mut CodegenResult,
|
|
struct_layout: &mut StructLayoutTracker,
|
|
_fields: &mut F,
|
|
methods: &mut M,
|
|
(unit_field_name, bitfield_representable_as_int, bitfield_visibility): (
|
|
&'a str,
|
|
&mut bool,
|
|
&'a mut FieldVisibilityKind,
|
|
),
|
|
) where
|
|
F: Extend<proc_macro2::TokenStream>,
|
|
M: Extend<proc_macro2::TokenStream>,
|
|
{
|
|
let prefix = ctx.trait_prefix();
|
|
let getter_name = bitfield_getter_name(ctx, self);
|
|
let setter_name = bitfield_setter_name(ctx, self);
|
|
let unit_field_ident = Ident::new(unit_field_name, Span::call_site());
|
|
|
|
let bitfield_ty_item = ctx.resolve_item(self.ty());
|
|
let bitfield_ty = bitfield_ty_item.expect_type();
|
|
|
|
let bitfield_ty_layout = bitfield_ty
|
|
.layout(ctx)
|
|
.expect("Bitfield without layout? Gah!");
|
|
let bitfield_int_ty =
|
|
match helpers::integer_type(ctx, bitfield_ty_layout) {
|
|
Some(int_ty) => {
|
|
*bitfield_representable_as_int = true;
|
|
int_ty
|
|
}
|
|
None => {
|
|
*bitfield_representable_as_int = false;
|
|
return;
|
|
}
|
|
};
|
|
|
|
let bitfield_ty =
|
|
bitfield_ty.to_rust_ty_or_opaque(ctx, bitfield_ty_item);
|
|
|
|
let offset = self.offset_into_unit();
|
|
let width = self.width() as u8;
|
|
|
|
let override_visibility = self.name().and_then(|field_name| {
|
|
ctx.options().last_callback(|cb| {
|
|
cb.field_visibility(FieldInfo {
|
|
type_name: &parent_item.canonical_name(ctx),
|
|
field_name,
|
|
})
|
|
})
|
|
});
|
|
*bitfield_visibility = compute_visibility(
|
|
ctx,
|
|
self.is_public(),
|
|
override_visibility,
|
|
self.annotations(),
|
|
visibility_kind,
|
|
);
|
|
let access_spec = access_specifier(*bitfield_visibility);
|
|
|
|
if parent.is_union() && !struct_layout.is_rust_union() {
|
|
methods.extend(Some(quote! {
|
|
#[inline]
|
|
#access_spec fn #getter_name(&self) -> #bitfield_ty {
|
|
unsafe {
|
|
::#prefix::mem::transmute(
|
|
self.#unit_field_ident.as_ref().get(#offset, #width)
|
|
as #bitfield_int_ty
|
|
)
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
#access_spec fn #setter_name(&mut self, val: #bitfield_ty) {
|
|
unsafe {
|
|
let val: #bitfield_int_ty = ::#prefix::mem::transmute(val);
|
|
self.#unit_field_ident.as_mut().set(
|
|
#offset,
|
|
#width,
|
|
val as u64
|
|
)
|
|
}
|
|
}
|
|
}));
|
|
} else {
|
|
methods.extend(Some(quote! {
|
|
#[inline]
|
|
#access_spec fn #getter_name(&self) -> #bitfield_ty {
|
|
unsafe {
|
|
::#prefix::mem::transmute(
|
|
self.#unit_field_ident.get(#offset, #width)
|
|
as #bitfield_int_ty
|
|
)
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
#access_spec fn #setter_name(&mut self, val: #bitfield_ty) {
|
|
unsafe {
|
|
let val: #bitfield_int_ty = ::#prefix::mem::transmute(val);
|
|
self.#unit_field_ident.set(
|
|
#offset,
|
|
#width,
|
|
val as u64
|
|
)
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CodeGenerator for CompInfo {
|
|
type Extra = Item;
|
|
type Return = ();
|
|
|
|
fn codegen(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
result: &mut CodegenResult<'_>,
|
|
item: &Item,
|
|
) {
|
|
debug!("<CompInfo as CodeGenerator>::codegen: item = {:?}", item);
|
|
debug_assert!(item.is_enabled_for_codegen(ctx));
|
|
|
|
// Don't output classes with template parameters that aren't types, and
|
|
// also don't output template specializations, neither total or partial.
|
|
if self.has_non_type_template_params() {
|
|
return;
|
|
}
|
|
|
|
let ty = item.expect_type();
|
|
let layout = ty.layout(ctx);
|
|
let mut packed = self.is_packed(ctx, layout.as_ref());
|
|
|
|
let canonical_name = item.canonical_name(ctx);
|
|
let canonical_ident = ctx.rust_ident(&canonical_name);
|
|
|
|
// Generate the vtable from the method list if appropriate.
|
|
//
|
|
// TODO: I don't know how this could play with virtual methods that are
|
|
// not in the list of methods found by us, we'll see. Also, could the
|
|
// order of the vtable pointers vary?
|
|
//
|
|
// FIXME: Once we generate proper vtables, we need to codegen the
|
|
// vtable, but *not* generate a field for it in the case that
|
|
// HasVtable::has_vtable_ptr is false but HasVtable::has_vtable is true.
|
|
//
|
|
// Also, we need to generate the vtable in such a way it "inherits" from
|
|
// the parent too.
|
|
let is_opaque = item.is_opaque(ctx, &());
|
|
let mut fields = vec![];
|
|
let visibility = item
|
|
.annotations()
|
|
.visibility_kind()
|
|
.unwrap_or(ctx.options().default_visibility);
|
|
let mut struct_layout = StructLayoutTracker::new(
|
|
ctx,
|
|
self,
|
|
ty,
|
|
&canonical_name,
|
|
visibility,
|
|
);
|
|
|
|
if !is_opaque {
|
|
if item.has_vtable_ptr(ctx) {
|
|
let vtable = Vtable::new(item.id(), self);
|
|
vtable.codegen(ctx, result, item);
|
|
|
|
let vtable_type = vtable
|
|
.try_to_rust_ty(ctx, &())
|
|
.expect("vtable to Rust type conversion is infallible")
|
|
.to_ptr(true);
|
|
|
|
fields.push(quote! {
|
|
pub vtable_: #vtable_type ,
|
|
});
|
|
|
|
struct_layout.saw_vtable();
|
|
}
|
|
|
|
for base in self.base_members() {
|
|
if !base.requires_storage(ctx) {
|
|
continue;
|
|
}
|
|
|
|
let inner_item = ctx.resolve_item(base.ty);
|
|
let inner = inner_item
|
|
.to_rust_ty_or_opaque(ctx, &())
|
|
.with_implicit_template_params(ctx, inner_item);
|
|
let field_name = ctx.rust_ident(&base.field_name);
|
|
|
|
struct_layout.saw_base(inner_item.expect_type());
|
|
|
|
let visibility = match (
|
|
base.is_public(),
|
|
ctx.options().respect_cxx_access_specs,
|
|
) {
|
|
(true, true) => FieldVisibilityKind::Public,
|
|
(false, true) => FieldVisibilityKind::Private,
|
|
_ => ctx.options().default_visibility,
|
|
};
|
|
|
|
let access_spec = access_specifier(visibility);
|
|
fields.push(quote! {
|
|
#access_spec #field_name: #inner,
|
|
});
|
|
}
|
|
}
|
|
|
|
let mut methods = vec![];
|
|
if !is_opaque {
|
|
let struct_accessor_kind = item
|
|
.annotations()
|
|
.accessor_kind()
|
|
.unwrap_or(FieldAccessorKind::None);
|
|
for field in self.fields() {
|
|
field.codegen(
|
|
ctx,
|
|
visibility,
|
|
struct_accessor_kind,
|
|
self,
|
|
item,
|
|
result,
|
|
&mut struct_layout,
|
|
&mut fields,
|
|
&mut methods,
|
|
(),
|
|
);
|
|
}
|
|
// Check whether an explicit padding field is needed
|
|
// at the end.
|
|
if let Some(comp_layout) = layout {
|
|
fields.extend(
|
|
struct_layout
|
|
.add_tail_padding(&canonical_name, comp_layout),
|
|
);
|
|
}
|
|
}
|
|
|
|
if is_opaque {
|
|
// Opaque item should not have generated methods, fields.
|
|
debug_assert!(fields.is_empty());
|
|
debug_assert!(methods.is_empty());
|
|
}
|
|
|
|
let is_union = self.kind() == CompKind::Union;
|
|
let layout = item.kind().expect_type().layout(ctx);
|
|
let zero_sized = item.is_zero_sized(ctx);
|
|
let forward_decl = self.is_forward_declaration();
|
|
|
|
let mut explicit_align = None;
|
|
|
|
// C++ requires every struct to be addressable, so what C++ compilers do
|
|
// is making the struct 1-byte sized.
|
|
//
|
|
// This is apparently not the case for C, see:
|
|
// https://github.com/rust-lang/rust-bindgen/issues/551
|
|
//
|
|
// Just get the layout, and assume C++ if not.
|
|
//
|
|
// NOTE: This check is conveniently here to avoid the dummy fields we
|
|
// may add for unused template parameters.
|
|
if !forward_decl && zero_sized {
|
|
let has_address = if is_opaque {
|
|
// Generate the address field if it's an opaque type and
|
|
// couldn't determine the layout of the blob.
|
|
layout.is_none()
|
|
} else {
|
|
layout.map_or(true, |l| l.size != 0)
|
|
};
|
|
|
|
if has_address {
|
|
let layout = Layout::new(1, 1);
|
|
let ty = helpers::blob(ctx, Layout::new(1, 1));
|
|
struct_layout.saw_field_with_layout(
|
|
"_address",
|
|
layout,
|
|
/* offset = */ Some(0),
|
|
);
|
|
fields.push(quote! {
|
|
pub _address: #ty,
|
|
});
|
|
}
|
|
}
|
|
|
|
if is_opaque {
|
|
match layout {
|
|
Some(l) => {
|
|
explicit_align = Some(l.align);
|
|
|
|
let ty = helpers::blob(ctx, l);
|
|
fields.push(quote! {
|
|
pub _bindgen_opaque_blob: #ty ,
|
|
});
|
|
}
|
|
None => {
|
|
warn!("Opaque type without layout! Expect dragons!");
|
|
}
|
|
}
|
|
} else if !is_union && !zero_sized {
|
|
if let Some(padding_field) =
|
|
layout.and_then(|layout| struct_layout.pad_struct(layout))
|
|
{
|
|
fields.push(padding_field);
|
|
}
|
|
|
|
if let Some(layout) = layout {
|
|
if struct_layout.requires_explicit_align(layout) {
|
|
if layout.align == 1 {
|
|
packed = true;
|
|
} else {
|
|
explicit_align = Some(layout.align);
|
|
if !ctx.options().rust_features.repr_align {
|
|
let ty = helpers::blob(
|
|
ctx,
|
|
Layout::new(0, layout.align),
|
|
);
|
|
fields.push(quote! {
|
|
pub __bindgen_align: #ty ,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if is_union && !forward_decl {
|
|
// TODO(emilio): It'd be nice to unify this with the struct path
|
|
// above somehow.
|
|
let layout = layout.expect("Unable to get layout information?");
|
|
if struct_layout.requires_explicit_align(layout) {
|
|
explicit_align = Some(layout.align);
|
|
}
|
|
|
|
if !struct_layout.is_rust_union() {
|
|
let ty = helpers::blob(ctx, layout);
|
|
fields.push(quote! {
|
|
pub bindgen_union_field: #ty ,
|
|
})
|
|
}
|
|
}
|
|
|
|
if forward_decl {
|
|
fields.push(quote! {
|
|
_unused: [u8; 0],
|
|
});
|
|
}
|
|
|
|
let mut generic_param_names = vec![];
|
|
|
|
for (idx, ty) in item.used_template_params(ctx).iter().enumerate() {
|
|
let param = ctx.resolve_type(*ty);
|
|
let name = param.name().unwrap();
|
|
let ident = ctx.rust_ident(name);
|
|
generic_param_names.push(ident.clone());
|
|
|
|
let prefix = ctx.trait_prefix();
|
|
let field_name = ctx.rust_ident(format!("_phantom_{}", idx));
|
|
fields.push(quote! {
|
|
pub #field_name : ::#prefix::marker::PhantomData<
|
|
::#prefix::cell::UnsafeCell<#ident>
|
|
> ,
|
|
});
|
|
}
|
|
|
|
let generics = if !generic_param_names.is_empty() {
|
|
let generic_param_names = generic_param_names.clone();
|
|
quote! {
|
|
< #( #generic_param_names ),* >
|
|
}
|
|
} else {
|
|
quote! {}
|
|
};
|
|
|
|
let mut attributes = vec![];
|
|
let mut needs_clone_impl = false;
|
|
let mut needs_default_impl = false;
|
|
let mut needs_debug_impl = false;
|
|
let mut needs_partialeq_impl = false;
|
|
if let Some(comment) = item.comment(ctx) {
|
|
attributes.push(attributes::doc(comment));
|
|
}
|
|
if packed && !is_opaque {
|
|
let n = layout.map_or(1, |l| l.align);
|
|
assert!(ctx.options().rust_features().repr_packed_n || n == 1);
|
|
let packed_repr = if n == 1 {
|
|
"packed".to_string()
|
|
} else {
|
|
format!("packed({})", n)
|
|
};
|
|
attributes.push(attributes::repr_list(&["C", &packed_repr]));
|
|
} else {
|
|
attributes.push(attributes::repr("C"));
|
|
}
|
|
|
|
if ctx.options().rust_features().repr_align {
|
|
if let Some(explicit) = explicit_align {
|
|
// Ensure that the struct has the correct alignment even in
|
|
// presence of alignas.
|
|
let explicit = helpers::ast_ty::int_expr(explicit as i64);
|
|
attributes.push(quote! {
|
|
#[repr(align(#explicit))]
|
|
});
|
|
}
|
|
}
|
|
|
|
let derivable_traits = derives_of_item(item, ctx, packed);
|
|
if !derivable_traits.contains(DerivableTraits::DEBUG) {
|
|
needs_debug_impl = ctx.options().derive_debug &&
|
|
ctx.options().impl_debug &&
|
|
!ctx.no_debug_by_name(item) &&
|
|
!item.annotations().disallow_debug();
|
|
}
|
|
|
|
if !derivable_traits.contains(DerivableTraits::DEFAULT) {
|
|
needs_default_impl = ctx.options().derive_default &&
|
|
!self.is_forward_declaration() &&
|
|
!ctx.no_default_by_name(item) &&
|
|
!item.annotations().disallow_default();
|
|
}
|
|
|
|
let all_template_params = item.all_template_params(ctx);
|
|
|
|
if derivable_traits.contains(DerivableTraits::COPY) &&
|
|
!derivable_traits.contains(DerivableTraits::CLONE)
|
|
{
|
|
needs_clone_impl = true;
|
|
}
|
|
|
|
if !derivable_traits.contains(DerivableTraits::PARTIAL_EQ) {
|
|
needs_partialeq_impl = ctx.options().derive_partialeq &&
|
|
ctx.options().impl_partialeq &&
|
|
ctx.lookup_can_derive_partialeq_or_partialord(item.id()) ==
|
|
CanDerive::Manually;
|
|
}
|
|
|
|
let mut derives: Vec<_> = derivable_traits.into();
|
|
derives.extend(item.annotations().derives().iter().map(String::as_str));
|
|
|
|
let is_rust_union = is_union && struct_layout.is_rust_union();
|
|
|
|
// The custom derives callback may return a list of derive attributes;
|
|
// add them to the end of the list.
|
|
let custom_derives = ctx.options().all_callbacks(|cb| {
|
|
cb.add_derives(&DeriveInfo {
|
|
name: &canonical_name,
|
|
kind: if is_rust_union {
|
|
DeriveTypeKind::Union
|
|
} else {
|
|
DeriveTypeKind::Struct
|
|
},
|
|
})
|
|
});
|
|
// In most cases this will be a no-op, since custom_derives will be empty.
|
|
derives.extend(custom_derives.iter().map(|s| s.as_str()));
|
|
|
|
if !derives.is_empty() {
|
|
attributes.push(attributes::derives(&derives))
|
|
}
|
|
|
|
if item.must_use(ctx) {
|
|
attributes.push(attributes::must_use());
|
|
}
|
|
|
|
let mut tokens = if is_rust_union {
|
|
quote! {
|
|
#( #attributes )*
|
|
pub union #canonical_ident
|
|
}
|
|
} else {
|
|
quote! {
|
|
#( #attributes )*
|
|
pub struct #canonical_ident
|
|
}
|
|
};
|
|
|
|
tokens.append_all(quote! {
|
|
#generics {
|
|
#( #fields )*
|
|
}
|
|
});
|
|
result.push(tokens);
|
|
|
|
// Generate the inner types and all that stuff.
|
|
//
|
|
// TODO: In the future we might want to be smart, and use nested
|
|
// modules, and whatnot.
|
|
for ty in self.inner_types() {
|
|
let child_item = ctx.resolve_item(*ty);
|
|
// assert_eq!(child_item.parent_id(), item.id());
|
|
child_item.codegen(ctx, result, &());
|
|
}
|
|
|
|
// NOTE: Some unexposed attributes (like alignment attributes) may
|
|
// affect layout, so we're bad and pray to the gods for avoid sending
|
|
// all the tests to shit when parsing things like max_align_t.
|
|
if self.found_unknown_attr() {
|
|
warn!(
|
|
"Type {} has an unknown attribute that may affect layout",
|
|
canonical_ident
|
|
);
|
|
}
|
|
|
|
if all_template_params.is_empty() {
|
|
if !is_opaque {
|
|
for var in self.inner_vars() {
|
|
ctx.resolve_item(*var).codegen(ctx, result, &());
|
|
}
|
|
}
|
|
|
|
if ctx.options().layout_tests && !self.is_forward_declaration() {
|
|
if let Some(layout) = layout {
|
|
let fn_name =
|
|
format!("bindgen_test_layout_{}", canonical_ident);
|
|
let fn_name = ctx.rust_ident_raw(fn_name);
|
|
let prefix = ctx.trait_prefix();
|
|
let size_of_expr = quote! {
|
|
::#prefix::mem::size_of::<#canonical_ident>()
|
|
};
|
|
let align_of_expr = quote! {
|
|
::#prefix::mem::align_of::<#canonical_ident>()
|
|
};
|
|
let size = layout.size;
|
|
let align = layout.align;
|
|
|
|
let check_struct_align = if align >
|
|
ctx.target_pointer_size() &&
|
|
!ctx.options().rust_features().repr_align
|
|
{
|
|
None
|
|
} else {
|
|
Some(quote! {
|
|
assert_eq!(#align_of_expr,
|
|
#align,
|
|
concat!("Alignment of ", stringify!(#canonical_ident)));
|
|
|
|
})
|
|
};
|
|
|
|
let should_skip_field_offset_checks = is_opaque;
|
|
|
|
let check_field_offset = if should_skip_field_offset_checks
|
|
{
|
|
vec![]
|
|
} else {
|
|
self.fields()
|
|
.iter()
|
|
.filter_map(|field| match *field {
|
|
Field::DataMember(ref f) if f.name().is_some() => Some(f),
|
|
_ => None,
|
|
})
|
|
.flat_map(|field| {
|
|
let name = field.name().unwrap();
|
|
field.offset().map(|offset| {
|
|
let field_offset = offset / 8;
|
|
let field_name = ctx.rust_ident(name);
|
|
quote! {
|
|
assert_eq!(
|
|
unsafe {
|
|
::#prefix::ptr::addr_of!((*ptr).#field_name) as usize - ptr as usize
|
|
},
|
|
#field_offset,
|
|
concat!("Offset of field: ", stringify!(#canonical_ident), "::", stringify!(#field_name))
|
|
);
|
|
}
|
|
})
|
|
})
|
|
.collect()
|
|
};
|
|
|
|
let uninit_decl = if !check_field_offset.is_empty() {
|
|
// FIXME: When MSRV >= 1.59.0, we can use
|
|
// > const PTR: *const #canonical_ident = ::#prefix::mem::MaybeUninit::uninit().as_ptr();
|
|
Some(quote! {
|
|
// Use a shared MaybeUninit so that rustc with
|
|
// opt-level=0 doesn't take too much stack space,
|
|
// see #2218.
|
|
const UNINIT: ::#prefix::mem::MaybeUninit<#canonical_ident> = ::#prefix::mem::MaybeUninit::uninit();
|
|
let ptr = UNINIT.as_ptr();
|
|
})
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let item = quote! {
|
|
#[test]
|
|
fn #fn_name() {
|
|
#uninit_decl
|
|
assert_eq!(#size_of_expr,
|
|
#size,
|
|
concat!("Size of: ", stringify!(#canonical_ident)));
|
|
#check_struct_align
|
|
#( #check_field_offset )*
|
|
}
|
|
};
|
|
result.push(item);
|
|
}
|
|
}
|
|
|
|
let mut method_names = Default::default();
|
|
if ctx.options().codegen_config.methods() {
|
|
for method in self.methods() {
|
|
assert!(method.kind() != MethodKind::Constructor);
|
|
method.codegen_method(
|
|
ctx,
|
|
&mut methods,
|
|
&mut method_names,
|
|
result,
|
|
self,
|
|
);
|
|
}
|
|
}
|
|
|
|
if ctx.options().codegen_config.constructors() {
|
|
for sig in self.constructors() {
|
|
Method::new(
|
|
MethodKind::Constructor,
|
|
*sig,
|
|
/* const */
|
|
false,
|
|
)
|
|
.codegen_method(
|
|
ctx,
|
|
&mut methods,
|
|
&mut method_names,
|
|
result,
|
|
self,
|
|
);
|
|
}
|
|
}
|
|
|
|
if ctx.options().codegen_config.destructors() {
|
|
if let Some((kind, destructor)) = self.destructor() {
|
|
debug_assert!(kind.is_destructor());
|
|
Method::new(kind, destructor, false).codegen_method(
|
|
ctx,
|
|
&mut methods,
|
|
&mut method_names,
|
|
result,
|
|
self,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// NB: We can't use to_rust_ty here since for opaque types this tries to
|
|
// use the specialization knowledge to generate a blob field.
|
|
let ty_for_impl = quote! {
|
|
#canonical_ident #generics
|
|
};
|
|
|
|
if needs_clone_impl {
|
|
result.push(quote! {
|
|
impl #generics Clone for #ty_for_impl {
|
|
fn clone(&self) -> Self { *self }
|
|
}
|
|
});
|
|
}
|
|
|
|
if needs_default_impl {
|
|
let prefix = ctx.trait_prefix();
|
|
let body = if ctx.options().rust_features().maybe_uninit {
|
|
quote! {
|
|
let mut s = ::#prefix::mem::MaybeUninit::<Self>::uninit();
|
|
unsafe {
|
|
::#prefix::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
|
|
s.assume_init()
|
|
}
|
|
}
|
|
} else {
|
|
quote! {
|
|
unsafe {
|
|
let mut s: Self = ::#prefix::mem::uninitialized();
|
|
::#prefix::ptr::write_bytes(&mut s, 0, 1);
|
|
s
|
|
}
|
|
}
|
|
};
|
|
// Note we use `ptr::write_bytes()` instead of `mem::zeroed()` because the latter does
|
|
// not necessarily ensure padding bytes are zeroed. Some C libraries are sensitive to
|
|
// non-zero padding bytes, especially when forwards/backwards compatability is
|
|
// involved.
|
|
result.push(quote! {
|
|
impl #generics Default for #ty_for_impl {
|
|
fn default() -> Self {
|
|
#body
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
if needs_debug_impl {
|
|
let impl_ = impl_debug::gen_debug_impl(
|
|
ctx,
|
|
self.fields(),
|
|
item,
|
|
self.kind(),
|
|
);
|
|
|
|
let prefix = ctx.trait_prefix();
|
|
|
|
result.push(quote! {
|
|
impl #generics ::#prefix::fmt::Debug for #ty_for_impl {
|
|
#impl_
|
|
}
|
|
});
|
|
}
|
|
|
|
if needs_partialeq_impl {
|
|
if let Some(impl_) = impl_partialeq::gen_partialeq_impl(
|
|
ctx,
|
|
self,
|
|
item,
|
|
&ty_for_impl,
|
|
) {
|
|
let partialeq_bounds = if !generic_param_names.is_empty() {
|
|
let bounds = generic_param_names.iter().map(|t| {
|
|
quote! { #t: PartialEq }
|
|
});
|
|
quote! { where #( #bounds ),* }
|
|
} else {
|
|
quote! {}
|
|
};
|
|
|
|
let prefix = ctx.trait_prefix();
|
|
result.push(quote! {
|
|
impl #generics ::#prefix::cmp::PartialEq for #ty_for_impl #partialeq_bounds {
|
|
#impl_
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
if !methods.is_empty() {
|
|
result.push(quote! {
|
|
impl #generics #ty_for_impl {
|
|
#( #methods )*
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Method {
|
|
fn codegen_method(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
methods: &mut Vec<proc_macro2::TokenStream>,
|
|
method_names: &mut HashSet<String>,
|
|
result: &mut CodegenResult<'_>,
|
|
_parent: &CompInfo,
|
|
) {
|
|
assert!({
|
|
let cc = &ctx.options().codegen_config;
|
|
match self.kind() {
|
|
MethodKind::Constructor => cc.constructors(),
|
|
MethodKind::Destructor => cc.destructors(),
|
|
MethodKind::VirtualDestructor { .. } => cc.destructors(),
|
|
MethodKind::Static |
|
|
MethodKind::Normal |
|
|
MethodKind::Virtual { .. } => cc.methods(),
|
|
}
|
|
});
|
|
|
|
// TODO(emilio): We could generate final stuff at least.
|
|
if self.is_virtual() {
|
|
return; // FIXME
|
|
}
|
|
|
|
// First of all, output the actual function.
|
|
let function_item = ctx.resolve_item(self.signature());
|
|
if !function_item.process_before_codegen(ctx, result) {
|
|
return;
|
|
}
|
|
let function = function_item.expect_function();
|
|
let times_seen = function.codegen(ctx, result, function_item);
|
|
let times_seen = match times_seen {
|
|
Some(seen) => seen,
|
|
None => return,
|
|
};
|
|
let signature_item = ctx.resolve_item(function.signature());
|
|
let mut name = match self.kind() {
|
|
MethodKind::Constructor => "new".into(),
|
|
MethodKind::Destructor => "destruct".into(),
|
|
_ => function.name().to_owned(),
|
|
};
|
|
|
|
let signature = match *signature_item.expect_type().kind() {
|
|
TypeKind::Function(ref sig) => sig,
|
|
_ => panic!("How in the world?"),
|
|
};
|
|
|
|
let supported_abi = signature.abi(ctx, Some(&*name)).is_ok();
|
|
if !supported_abi {
|
|
return;
|
|
}
|
|
|
|
// Do not generate variadic methods, since rust does not allow
|
|
// implementing them, and we don't do a good job at it anyway.
|
|
if signature.is_variadic() {
|
|
return;
|
|
}
|
|
|
|
if method_names.contains(&name) {
|
|
let mut count = 1;
|
|
let mut new_name;
|
|
|
|
while {
|
|
new_name = format!("{}{}", name, count);
|
|
method_names.contains(&new_name)
|
|
} {
|
|
count += 1;
|
|
}
|
|
|
|
name = new_name;
|
|
}
|
|
|
|
method_names.insert(name.clone());
|
|
|
|
let mut function_name = function_item.canonical_name(ctx);
|
|
if times_seen > 0 {
|
|
write!(&mut function_name, "{}", times_seen).unwrap();
|
|
}
|
|
let function_name = ctx.rust_ident(function_name);
|
|
let mut args = utils::fnsig_arguments(ctx, signature);
|
|
let mut ret = utils::fnsig_return_ty(ctx, signature);
|
|
|
|
if !self.is_static() && !self.is_constructor() {
|
|
args[0] = if self.is_const() {
|
|
quote! { &self }
|
|
} else {
|
|
quote! { &mut self }
|
|
};
|
|
}
|
|
|
|
// If it's a constructor, we always return `Self`, and we inject the
|
|
// "this" parameter, so there's no need to ask the user for it.
|
|
//
|
|
// Note that constructors in Clang are represented as functions with
|
|
// return-type = void.
|
|
if self.is_constructor() {
|
|
args.remove(0);
|
|
ret = quote! { -> Self };
|
|
}
|
|
|
|
let mut exprs =
|
|
helpers::ast_ty::arguments_from_signature(signature, ctx);
|
|
|
|
let mut stmts = vec![];
|
|
|
|
// If it's a constructor, we need to insert an extra parameter with a
|
|
// variable called `__bindgen_tmp` we're going to create.
|
|
if self.is_constructor() {
|
|
let prefix = ctx.trait_prefix();
|
|
let tmp_variable_decl = if ctx
|
|
.options()
|
|
.rust_features()
|
|
.maybe_uninit
|
|
{
|
|
exprs[0] = quote! {
|
|
__bindgen_tmp.as_mut_ptr()
|
|
};
|
|
quote! {
|
|
let mut __bindgen_tmp = ::#prefix::mem::MaybeUninit::uninit()
|
|
}
|
|
} else {
|
|
exprs[0] = quote! {
|
|
&mut __bindgen_tmp
|
|
};
|
|
quote! {
|
|
let mut __bindgen_tmp = ::#prefix::mem::uninitialized()
|
|
}
|
|
};
|
|
stmts.push(tmp_variable_decl);
|
|
} else if !self.is_static() {
|
|
assert!(!exprs.is_empty());
|
|
exprs[0] = quote! {
|
|
self
|
|
};
|
|
};
|
|
|
|
let call = quote! {
|
|
#function_name (#( #exprs ),* )
|
|
};
|
|
|
|
stmts.push(call);
|
|
|
|
if self.is_constructor() {
|
|
stmts.push(if ctx.options().rust_features().maybe_uninit {
|
|
quote! {
|
|
__bindgen_tmp.assume_init()
|
|
}
|
|
} else {
|
|
quote! {
|
|
__bindgen_tmp
|
|
}
|
|
})
|
|
}
|
|
|
|
let block = ctx.wrap_unsafe_ops(quote! ( #( #stmts );*));
|
|
|
|
let mut attrs = vec![attributes::inline()];
|
|
|
|
if signature.must_use() &&
|
|
ctx.options().rust_features().must_use_function
|
|
{
|
|
attrs.push(attributes::must_use());
|
|
}
|
|
|
|
let name = ctx.rust_ident(&name);
|
|
methods.push(quote! {
|
|
#(#attrs)*
|
|
pub unsafe fn #name ( #( #args ),* ) #ret {
|
|
#block
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/// A helper type that represents different enum variations.
|
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
pub enum EnumVariation {
|
|
/// The code for this enum will use a Rust enum. Note that creating this in unsafe code
|
|
/// (including FFI) with an invalid value will invoke undefined behaviour, whether or not
|
|
/// its marked as non_exhaustive.
|
|
Rust {
|
|
/// Indicates whether the generated struct should be `#[non_exhaustive]`
|
|
non_exhaustive: bool,
|
|
},
|
|
/// The code for this enum will use a newtype
|
|
NewType {
|
|
/// Indicates whether the newtype will have bitwise operators
|
|
is_bitfield: bool,
|
|
/// Indicates whether the variants will be represented as global constants
|
|
is_global: bool,
|
|
},
|
|
/// The code for this enum will use consts
|
|
Consts,
|
|
/// The code for this enum will use a module containing consts
|
|
ModuleConsts,
|
|
}
|
|
|
|
impl EnumVariation {
|
|
fn is_rust(&self) -> bool {
|
|
matches!(*self, EnumVariation::Rust { .. })
|
|
}
|
|
|
|
/// Both the `Const` and `ModuleConsts` variants will cause this to return
|
|
/// true.
|
|
fn is_const(&self) -> bool {
|
|
matches!(*self, EnumVariation::Consts | EnumVariation::ModuleConsts)
|
|
}
|
|
}
|
|
|
|
impl Default for EnumVariation {
|
|
fn default() -> EnumVariation {
|
|
EnumVariation::Consts
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for EnumVariation {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let s = match self {
|
|
Self::Rust {
|
|
non_exhaustive: false,
|
|
} => "rust",
|
|
Self::Rust {
|
|
non_exhaustive: true,
|
|
} => "rust_non_exhaustive",
|
|
Self::NewType {
|
|
is_bitfield: true, ..
|
|
} => "bitfield",
|
|
Self::NewType {
|
|
is_bitfield: false,
|
|
is_global,
|
|
} => {
|
|
if *is_global {
|
|
"newtype_global"
|
|
} else {
|
|
"newtype"
|
|
}
|
|
}
|
|
Self::Consts => "consts",
|
|
Self::ModuleConsts => "moduleconsts",
|
|
};
|
|
s.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl std::str::FromStr for EnumVariation {
|
|
type Err = std::io::Error;
|
|
|
|
/// Create a `EnumVariation` from a string.
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s {
|
|
"rust" => Ok(EnumVariation::Rust {
|
|
non_exhaustive: false,
|
|
}),
|
|
"rust_non_exhaustive" => Ok(EnumVariation::Rust {
|
|
non_exhaustive: true,
|
|
}),
|
|
"bitfield" => Ok(EnumVariation::NewType {
|
|
is_bitfield: true,
|
|
is_global: false,
|
|
}),
|
|
"consts" => Ok(EnumVariation::Consts),
|
|
"moduleconsts" => Ok(EnumVariation::ModuleConsts),
|
|
"newtype" => Ok(EnumVariation::NewType {
|
|
is_bitfield: false,
|
|
is_global: false,
|
|
}),
|
|
"newtype_global" => Ok(EnumVariation::NewType {
|
|
is_bitfield: false,
|
|
is_global: true,
|
|
}),
|
|
_ => Err(std::io::Error::new(
|
|
std::io::ErrorKind::InvalidInput,
|
|
concat!(
|
|
"Got an invalid EnumVariation. Accepted values ",
|
|
"are 'rust', 'rust_non_exhaustive', 'bitfield', 'consts',",
|
|
"'moduleconsts', 'newtype' and 'newtype_global'."
|
|
),
|
|
)),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A helper type to construct different enum variations.
|
|
enum EnumBuilder<'a> {
|
|
Rust {
|
|
attrs: Vec<proc_macro2::TokenStream>,
|
|
ident: Ident,
|
|
tokens: proc_macro2::TokenStream,
|
|
emitted_any_variants: bool,
|
|
},
|
|
NewType {
|
|
canonical_name: &'a str,
|
|
tokens: proc_macro2::TokenStream,
|
|
is_bitfield: bool,
|
|
is_global: bool,
|
|
},
|
|
Consts {
|
|
variants: Vec<proc_macro2::TokenStream>,
|
|
},
|
|
ModuleConsts {
|
|
module_name: &'a str,
|
|
module_items: Vec<proc_macro2::TokenStream>,
|
|
},
|
|
}
|
|
|
|
impl<'a> EnumBuilder<'a> {
|
|
/// Returns true if the builder is for a rustified enum.
|
|
fn is_rust_enum(&self) -> bool {
|
|
matches!(*self, EnumBuilder::Rust { .. })
|
|
}
|
|
|
|
/// Create a new enum given an item builder, a canonical name, a name for
|
|
/// the representation, and which variation it should be generated as.
|
|
fn new(
|
|
name: &'a str,
|
|
mut attrs: Vec<proc_macro2::TokenStream>,
|
|
repr: syn::Type,
|
|
enum_variation: EnumVariation,
|
|
has_typedef: bool,
|
|
) -> Self {
|
|
let ident = Ident::new(name, Span::call_site());
|
|
|
|
match enum_variation {
|
|
EnumVariation::NewType {
|
|
is_bitfield,
|
|
is_global,
|
|
} => EnumBuilder::NewType {
|
|
canonical_name: name,
|
|
tokens: quote! {
|
|
#( #attrs )*
|
|
pub struct #ident (pub #repr);
|
|
},
|
|
is_bitfield,
|
|
is_global,
|
|
},
|
|
|
|
EnumVariation::Rust { .. } => {
|
|
// `repr` is guaranteed to be Rustified in Enum::codegen
|
|
attrs.insert(0, quote! { #[repr( #repr )] });
|
|
let tokens = quote!();
|
|
EnumBuilder::Rust {
|
|
attrs,
|
|
ident,
|
|
tokens,
|
|
emitted_any_variants: false,
|
|
}
|
|
}
|
|
|
|
EnumVariation::Consts => {
|
|
let mut variants = Vec::new();
|
|
|
|
if !has_typedef {
|
|
variants.push(quote! {
|
|
#( #attrs )*
|
|
pub type #ident = #repr;
|
|
});
|
|
}
|
|
|
|
EnumBuilder::Consts { variants }
|
|
}
|
|
|
|
EnumVariation::ModuleConsts => {
|
|
let ident = Ident::new(
|
|
CONSTIFIED_ENUM_MODULE_REPR_NAME,
|
|
Span::call_site(),
|
|
);
|
|
let type_definition = quote! {
|
|
#( #attrs )*
|
|
pub type #ident = #repr;
|
|
};
|
|
|
|
EnumBuilder::ModuleConsts {
|
|
module_name: name,
|
|
module_items: vec![type_definition],
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Add a variant to this enum.
|
|
fn with_variant(
|
|
self,
|
|
ctx: &BindgenContext,
|
|
variant: &EnumVariant,
|
|
mangling_prefix: Option<&str>,
|
|
rust_ty: syn::Type,
|
|
result: &mut CodegenResult<'_>,
|
|
is_ty_named: bool,
|
|
) -> Self {
|
|
let variant_name = ctx.rust_mangle(variant.name());
|
|
let is_rust_enum = self.is_rust_enum();
|
|
let expr = match variant.val() {
|
|
EnumVariantValue::Boolean(v) if is_rust_enum => {
|
|
helpers::ast_ty::uint_expr(v as u64)
|
|
}
|
|
EnumVariantValue::Boolean(v) => quote!(#v),
|
|
EnumVariantValue::Signed(v) => helpers::ast_ty::int_expr(v),
|
|
EnumVariantValue::Unsigned(v) => helpers::ast_ty::uint_expr(v),
|
|
};
|
|
|
|
let mut doc = quote! {};
|
|
if ctx.options().generate_comments {
|
|
if let Some(raw_comment) = variant.comment() {
|
|
let comment = ctx.options().process_comment(raw_comment);
|
|
doc = attributes::doc(comment);
|
|
}
|
|
}
|
|
|
|
match self {
|
|
EnumBuilder::Rust {
|
|
attrs,
|
|
ident,
|
|
tokens,
|
|
emitted_any_variants: _,
|
|
} => {
|
|
let name = ctx.rust_ident(variant_name);
|
|
EnumBuilder::Rust {
|
|
attrs,
|
|
ident,
|
|
tokens: quote! {
|
|
#tokens
|
|
#doc
|
|
#name = #expr,
|
|
},
|
|
emitted_any_variants: true,
|
|
}
|
|
}
|
|
|
|
EnumBuilder::NewType {
|
|
canonical_name,
|
|
is_global,
|
|
..
|
|
} => {
|
|
if ctx.options().rust_features().associated_const &&
|
|
is_ty_named &&
|
|
!is_global
|
|
{
|
|
let enum_ident = ctx.rust_ident(canonical_name);
|
|
let variant_ident = ctx.rust_ident(variant_name);
|
|
|
|
result.push(quote! {
|
|
impl #enum_ident {
|
|
#doc
|
|
pub const #variant_ident : #rust_ty = #rust_ty ( #expr );
|
|
}
|
|
});
|
|
} else {
|
|
let ident = ctx.rust_ident(match mangling_prefix {
|
|
Some(prefix) => {
|
|
Cow::Owned(format!("{}_{}", prefix, variant_name))
|
|
}
|
|
None => variant_name,
|
|
});
|
|
result.push(quote! {
|
|
#doc
|
|
pub const #ident : #rust_ty = #rust_ty ( #expr );
|
|
});
|
|
}
|
|
|
|
self
|
|
}
|
|
|
|
EnumBuilder::Consts { .. } => {
|
|
let constant_name = match mangling_prefix {
|
|
Some(prefix) => {
|
|
Cow::Owned(format!("{}_{}", prefix, variant_name))
|
|
}
|
|
None => variant_name,
|
|
};
|
|
|
|
let ident = ctx.rust_ident(constant_name);
|
|
result.push(quote! {
|
|
#doc
|
|
pub const #ident : #rust_ty = #expr ;
|
|
});
|
|
|
|
self
|
|
}
|
|
EnumBuilder::ModuleConsts {
|
|
module_name,
|
|
mut module_items,
|
|
} => {
|
|
let name = ctx.rust_ident(variant_name);
|
|
let ty = ctx.rust_ident(CONSTIFIED_ENUM_MODULE_REPR_NAME);
|
|
module_items.push(quote! {
|
|
#doc
|
|
pub const #name : #ty = #expr ;
|
|
});
|
|
|
|
EnumBuilder::ModuleConsts {
|
|
module_name,
|
|
module_items,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn build(
|
|
self,
|
|
ctx: &BindgenContext,
|
|
rust_ty: syn::Type,
|
|
result: &mut CodegenResult<'_>,
|
|
) -> proc_macro2::TokenStream {
|
|
match self {
|
|
EnumBuilder::Rust {
|
|
attrs,
|
|
ident,
|
|
tokens,
|
|
emitted_any_variants,
|
|
..
|
|
} => {
|
|
let variants = if !emitted_any_variants {
|
|
quote!(__bindgen_cannot_repr_c_on_empty_enum = 0)
|
|
} else {
|
|
tokens
|
|
};
|
|
|
|
quote! {
|
|
#( #attrs )*
|
|
pub enum #ident {
|
|
#variants
|
|
}
|
|
}
|
|
}
|
|
EnumBuilder::NewType {
|
|
canonical_name,
|
|
tokens,
|
|
is_bitfield,
|
|
..
|
|
} => {
|
|
if !is_bitfield {
|
|
return tokens;
|
|
}
|
|
|
|
let rust_ty_name = ctx.rust_ident_raw(canonical_name);
|
|
let prefix = ctx.trait_prefix();
|
|
|
|
result.push(quote! {
|
|
impl ::#prefix::ops::BitOr<#rust_ty> for #rust_ty {
|
|
type Output = Self;
|
|
|
|
#[inline]
|
|
fn bitor(self, other: Self) -> Self {
|
|
#rust_ty_name(self.0 | other.0)
|
|
}
|
|
}
|
|
});
|
|
|
|
result.push(quote! {
|
|
impl ::#prefix::ops::BitOrAssign for #rust_ty {
|
|
#[inline]
|
|
fn bitor_assign(&mut self, rhs: #rust_ty) {
|
|
self.0 |= rhs.0;
|
|
}
|
|
}
|
|
});
|
|
|
|
result.push(quote! {
|
|
impl ::#prefix::ops::BitAnd<#rust_ty> for #rust_ty {
|
|
type Output = Self;
|
|
|
|
#[inline]
|
|
fn bitand(self, other: Self) -> Self {
|
|
#rust_ty_name(self.0 & other.0)
|
|
}
|
|
}
|
|
});
|
|
|
|
result.push(quote! {
|
|
impl ::#prefix::ops::BitAndAssign for #rust_ty {
|
|
#[inline]
|
|
fn bitand_assign(&mut self, rhs: #rust_ty) {
|
|
self.0 &= rhs.0;
|
|
}
|
|
}
|
|
});
|
|
|
|
tokens
|
|
}
|
|
EnumBuilder::Consts { variants, .. } => quote! { #( #variants )* },
|
|
EnumBuilder::ModuleConsts {
|
|
module_items,
|
|
module_name,
|
|
..
|
|
} => {
|
|
let ident = ctx.rust_ident(module_name);
|
|
quote! {
|
|
pub mod #ident {
|
|
#( #module_items )*
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CodeGenerator for Enum {
|
|
type Extra = Item;
|
|
type Return = ();
|
|
|
|
fn codegen(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
result: &mut CodegenResult<'_>,
|
|
item: &Item,
|
|
) {
|
|
debug!("<Enum as CodeGenerator>::codegen: item = {:?}", item);
|
|
debug_assert!(item.is_enabled_for_codegen(ctx));
|
|
|
|
let name = item.canonical_name(ctx);
|
|
let ident = ctx.rust_ident(&name);
|
|
let enum_ty = item.expect_type();
|
|
let layout = enum_ty.layout(ctx);
|
|
let variation = self.computed_enum_variation(ctx, item);
|
|
|
|
let repr_translated;
|
|
let repr = match self.repr().map(|repr| ctx.resolve_type(repr)) {
|
|
Some(repr)
|
|
if !ctx.options().translate_enum_integer_types &&
|
|
!variation.is_rust() =>
|
|
{
|
|
repr
|
|
}
|
|
repr => {
|
|
// An enum's integer type is translated to a native Rust
|
|
// integer type in 3 cases:
|
|
// * the enum is Rustified and we need a translated type for
|
|
// the repr attribute
|
|
// * the representation couldn't be determined from the C source
|
|
// * it was explicitly requested as a bindgen option
|
|
|
|
let kind = match repr {
|
|
Some(repr) => match *repr.canonical_type(ctx).kind() {
|
|
TypeKind::Int(int_kind) => int_kind,
|
|
_ => panic!("Unexpected type as enum repr"),
|
|
},
|
|
None => {
|
|
warn!(
|
|
"Guessing type of enum! Forward declarations of enums \
|
|
shouldn't be legal!"
|
|
);
|
|
IntKind::Int
|
|
}
|
|
};
|
|
|
|
let signed = kind.is_signed();
|
|
let size = layout
|
|
.map(|l| l.size)
|
|
.or_else(|| kind.known_size())
|
|
.unwrap_or(0);
|
|
|
|
let translated = match (signed, size) {
|
|
(true, 1) => IntKind::I8,
|
|
(false, 1) => IntKind::U8,
|
|
(true, 2) => IntKind::I16,
|
|
(false, 2) => IntKind::U16,
|
|
(true, 4) => IntKind::I32,
|
|
(false, 4) => IntKind::U32,
|
|
(true, 8) => IntKind::I64,
|
|
(false, 8) => IntKind::U64,
|
|
_ => {
|
|
warn!(
|
|
"invalid enum decl: signed: {}, size: {}",
|
|
signed, size
|
|
);
|
|
IntKind::I32
|
|
}
|
|
};
|
|
|
|
repr_translated =
|
|
Type::new(None, None, TypeKind::Int(translated), false);
|
|
&repr_translated
|
|
}
|
|
};
|
|
|
|
let mut attrs = vec![];
|
|
|
|
// TODO(emilio): Delegate this to the builders?
|
|
match variation {
|
|
EnumVariation::Rust { non_exhaustive } => {
|
|
if non_exhaustive &&
|
|
ctx.options().rust_features().non_exhaustive
|
|
{
|
|
attrs.push(attributes::non_exhaustive());
|
|
} else if non_exhaustive &&
|
|
!ctx.options().rust_features().non_exhaustive
|
|
{
|
|
panic!("The rust target you're using doesn't seem to support non_exhaustive enums");
|
|
}
|
|
}
|
|
EnumVariation::NewType { .. } => {
|
|
if ctx.options().rust_features.repr_transparent {
|
|
attrs.push(attributes::repr("transparent"));
|
|
} else {
|
|
attrs.push(attributes::repr("C"));
|
|
}
|
|
}
|
|
_ => {}
|
|
};
|
|
|
|
if let Some(comment) = item.comment(ctx) {
|
|
attrs.push(attributes::doc(comment));
|
|
}
|
|
|
|
if item.must_use(ctx) {
|
|
attrs.push(attributes::must_use());
|
|
}
|
|
|
|
if !variation.is_const() {
|
|
let packed = false; // Enums can't be packed in Rust.
|
|
let mut derives = derives_of_item(item, ctx, packed);
|
|
// For backwards compat, enums always derive
|
|
// Clone/Eq/PartialEq/Hash, even if we don't generate those by
|
|
// default.
|
|
derives.insert(
|
|
DerivableTraits::CLONE |
|
|
DerivableTraits::HASH |
|
|
DerivableTraits::PARTIAL_EQ |
|
|
DerivableTraits::EQ,
|
|
);
|
|
let mut derives: Vec<_> = derives.into();
|
|
for derive in item.annotations().derives().iter() {
|
|
if !derives.contains(&derive.as_str()) {
|
|
derives.push(derive);
|
|
}
|
|
}
|
|
|
|
// The custom derives callback may return a list of derive attributes;
|
|
// add them to the end of the list.
|
|
let custom_derives = ctx.options().all_callbacks(|cb| {
|
|
cb.add_derives(&DeriveInfo {
|
|
name: &name,
|
|
kind: DeriveTypeKind::Enum,
|
|
})
|
|
});
|
|
// In most cases this will be a no-op, since custom_derives will be empty.
|
|
derives.extend(custom_derives.iter().map(|s| s.as_str()));
|
|
|
|
attrs.push(attributes::derives(&derives));
|
|
}
|
|
|
|
fn add_constant(
|
|
ctx: &BindgenContext,
|
|
enum_: &Type,
|
|
// Only to avoid recomputing every time.
|
|
enum_canonical_name: &Ident,
|
|
// May be the same as "variant" if it's because the
|
|
// enum is unnamed and we still haven't seen the
|
|
// value.
|
|
variant_name: &Ident,
|
|
referenced_name: &Ident,
|
|
enum_rust_ty: syn::Type,
|
|
result: &mut CodegenResult<'_>,
|
|
) {
|
|
let constant_name = if enum_.name().is_some() {
|
|
if ctx.options().prepend_enum_name {
|
|
format!("{}_{}", enum_canonical_name, variant_name)
|
|
} else {
|
|
format!("{}", variant_name)
|
|
}
|
|
} else {
|
|
format!("{}", variant_name)
|
|
};
|
|
let constant_name = ctx.rust_ident(constant_name);
|
|
|
|
result.push(quote! {
|
|
pub const #constant_name : #enum_rust_ty =
|
|
#enum_canonical_name :: #referenced_name ;
|
|
});
|
|
}
|
|
|
|
let repr = repr.to_rust_ty_or_opaque(ctx, item);
|
|
let has_typedef = ctx.is_enum_typedef_combo(item.id());
|
|
|
|
let mut builder =
|
|
EnumBuilder::new(&name, attrs, repr, variation, has_typedef);
|
|
|
|
// A map where we keep a value -> variant relation.
|
|
let mut seen_values = HashMap::<_, Ident>::default();
|
|
let enum_rust_ty = item.to_rust_ty_or_opaque(ctx, &());
|
|
let is_toplevel = item.is_toplevel(ctx);
|
|
|
|
// Used to mangle the constants we generate in the unnamed-enum case.
|
|
let parent_canonical_name = if is_toplevel {
|
|
None
|
|
} else {
|
|
Some(item.parent_id().canonical_name(ctx))
|
|
};
|
|
|
|
let constant_mangling_prefix = if ctx.options().prepend_enum_name {
|
|
if enum_ty.name().is_none() {
|
|
parent_canonical_name.as_deref()
|
|
} else {
|
|
Some(&*name)
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
|
|
// NB: We defer the creation of constified variants, in case we find
|
|
// another variant with the same value (which is the common thing to
|
|
// do).
|
|
let mut constified_variants = VecDeque::new();
|
|
|
|
let mut iter = self.variants().iter().peekable();
|
|
while let Some(variant) =
|
|
iter.next().or_else(|| constified_variants.pop_front())
|
|
{
|
|
if variant.hidden() {
|
|
continue;
|
|
}
|
|
|
|
if variant.force_constification() && iter.peek().is_some() {
|
|
constified_variants.push_back(variant);
|
|
continue;
|
|
}
|
|
|
|
match seen_values.entry(variant.val()) {
|
|
Entry::Occupied(ref entry) => {
|
|
if variation.is_rust() {
|
|
let variant_name = ctx.rust_mangle(variant.name());
|
|
let mangled_name =
|
|
if is_toplevel || enum_ty.name().is_some() {
|
|
variant_name
|
|
} else {
|
|
let parent_name =
|
|
parent_canonical_name.as_ref().unwrap();
|
|
|
|
Cow::Owned(format!(
|
|
"{}_{}",
|
|
parent_name, variant_name
|
|
))
|
|
};
|
|
|
|
let existing_variant_name = entry.get();
|
|
// Use associated constants for named enums.
|
|
if enum_ty.name().is_some() &&
|
|
ctx.options().rust_features().associated_const
|
|
{
|
|
let enum_canonical_name = &ident;
|
|
let variant_name =
|
|
ctx.rust_ident_raw(&*mangled_name);
|
|
result.push(quote! {
|
|
impl #enum_rust_ty {
|
|
pub const #variant_name : #enum_rust_ty =
|
|
#enum_canonical_name :: #existing_variant_name ;
|
|
}
|
|
});
|
|
} else {
|
|
add_constant(
|
|
ctx,
|
|
enum_ty,
|
|
&ident,
|
|
&Ident::new(&mangled_name, Span::call_site()),
|
|
existing_variant_name,
|
|
enum_rust_ty.clone(),
|
|
result,
|
|
);
|
|
}
|
|
} else {
|
|
builder = builder.with_variant(
|
|
ctx,
|
|
variant,
|
|
constant_mangling_prefix,
|
|
enum_rust_ty.clone(),
|
|
result,
|
|
enum_ty.name().is_some(),
|
|
);
|
|
}
|
|
}
|
|
Entry::Vacant(entry) => {
|
|
builder = builder.with_variant(
|
|
ctx,
|
|
variant,
|
|
constant_mangling_prefix,
|
|
enum_rust_ty.clone(),
|
|
result,
|
|
enum_ty.name().is_some(),
|
|
);
|
|
|
|
let variant_name = ctx.rust_ident(variant.name());
|
|
|
|
// If it's an unnamed enum, or constification is enforced,
|
|
// we also generate a constant so it can be properly
|
|
// accessed.
|
|
if (variation.is_rust() && enum_ty.name().is_none()) ||
|
|
variant.force_constification()
|
|
{
|
|
let mangled_name = if is_toplevel {
|
|
variant_name.clone()
|
|
} else {
|
|
let parent_name =
|
|
parent_canonical_name.as_ref().unwrap();
|
|
|
|
Ident::new(
|
|
&format!("{}_{}", parent_name, variant_name),
|
|
Span::call_site(),
|
|
)
|
|
};
|
|
|
|
add_constant(
|
|
ctx,
|
|
enum_ty,
|
|
&ident,
|
|
&mangled_name,
|
|
&variant_name,
|
|
enum_rust_ty.clone(),
|
|
result,
|
|
);
|
|
}
|
|
|
|
entry.insert(variant_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
let item = builder.build(ctx, enum_rust_ty, result);
|
|
result.push(item);
|
|
}
|
|
}
|
|
|
|
/// Enum for the default type of macro constants.
|
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
pub enum MacroTypeVariation {
|
|
/// Use i32 or i64
|
|
Signed,
|
|
/// Use u32 or u64
|
|
Unsigned,
|
|
}
|
|
|
|
impl fmt::Display for MacroTypeVariation {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let s = match self {
|
|
Self::Signed => "signed",
|
|
Self::Unsigned => "unsigned",
|
|
};
|
|
s.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl Default for MacroTypeVariation {
|
|
fn default() -> MacroTypeVariation {
|
|
MacroTypeVariation::Unsigned
|
|
}
|
|
}
|
|
|
|
impl std::str::FromStr for MacroTypeVariation {
|
|
type Err = std::io::Error;
|
|
|
|
/// Create a `MacroTypeVariation` from a string.
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s {
|
|
"signed" => Ok(MacroTypeVariation::Signed),
|
|
"unsigned" => Ok(MacroTypeVariation::Unsigned),
|
|
_ => Err(std::io::Error::new(
|
|
std::io::ErrorKind::InvalidInput,
|
|
concat!(
|
|
"Got an invalid MacroTypeVariation. Accepted values ",
|
|
"are 'signed' and 'unsigned'"
|
|
),
|
|
)),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Enum for how aliases should be translated.
|
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
pub enum AliasVariation {
|
|
/// Convert to regular Rust alias
|
|
TypeAlias,
|
|
/// Create a new type by wrapping the old type in a struct and using #[repr(transparent)]
|
|
NewType,
|
|
/// Same as NewStruct but also impl Deref to be able to use the methods of the wrapped type
|
|
NewTypeDeref,
|
|
}
|
|
|
|
impl fmt::Display for AliasVariation {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let s = match self {
|
|
Self::TypeAlias => "type_alias",
|
|
Self::NewType => "new_type",
|
|
Self::NewTypeDeref => "new_type_deref",
|
|
};
|
|
|
|
s.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl Default for AliasVariation {
|
|
fn default() -> AliasVariation {
|
|
AliasVariation::TypeAlias
|
|
}
|
|
}
|
|
|
|
impl std::str::FromStr for AliasVariation {
|
|
type Err = std::io::Error;
|
|
|
|
/// Create an `AliasVariation` from a string.
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s {
|
|
"type_alias" => Ok(AliasVariation::TypeAlias),
|
|
"new_type" => Ok(AliasVariation::NewType),
|
|
"new_type_deref" => Ok(AliasVariation::NewTypeDeref),
|
|
_ => Err(std::io::Error::new(
|
|
std::io::ErrorKind::InvalidInput,
|
|
concat!(
|
|
"Got an invalid AliasVariation. Accepted values ",
|
|
"are 'type_alias', 'new_type', and 'new_type_deref'"
|
|
),
|
|
)),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Enum for how non-`Copy` `union`s should be translated.
|
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
pub enum NonCopyUnionStyle {
|
|
/// Wrap members in a type generated by `bindgen`.
|
|
BindgenWrapper,
|
|
/// Wrap members in [`::core::mem::ManuallyDrop`].
|
|
///
|
|
/// Note: `ManuallyDrop` was stabilized in Rust 1.20.0, do not use it if your
|
|
/// MSRV is lower.
|
|
ManuallyDrop,
|
|
}
|
|
|
|
impl fmt::Display for NonCopyUnionStyle {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let s = match self {
|
|
Self::BindgenWrapper => "bindgen_wrapper",
|
|
Self::ManuallyDrop => "manually_drop",
|
|
};
|
|
|
|
s.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl Default for NonCopyUnionStyle {
|
|
fn default() -> Self {
|
|
Self::BindgenWrapper
|
|
}
|
|
}
|
|
|
|
impl std::str::FromStr for NonCopyUnionStyle {
|
|
type Err = std::io::Error;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
match s {
|
|
"bindgen_wrapper" => Ok(Self::BindgenWrapper),
|
|
"manually_drop" => Ok(Self::ManuallyDrop),
|
|
_ => Err(std::io::Error::new(
|
|
std::io::ErrorKind::InvalidInput,
|
|
concat!(
|
|
"Got an invalid NonCopyUnionStyle. Accepted values ",
|
|
"are 'bindgen_wrapper' and 'manually_drop'"
|
|
),
|
|
)),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Fallible conversion to an opaque blob.
|
|
///
|
|
/// Implementors of this trait should provide the `try_get_layout` method to
|
|
/// fallibly get this thing's layout, which the provided `try_to_opaque` trait
|
|
/// method will use to convert the `Layout` into an opaque blob Rust type.
|
|
pub(crate) trait TryToOpaque {
|
|
type Extra;
|
|
|
|
/// Get the layout for this thing, if one is available.
|
|
fn try_get_layout(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
extra: &Self::Extra,
|
|
) -> error::Result<Layout>;
|
|
|
|
/// Do not override this provided trait method.
|
|
fn try_to_opaque(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
extra: &Self::Extra,
|
|
) -> error::Result<syn::Type> {
|
|
self.try_get_layout(ctx, extra)
|
|
.map(|layout| helpers::blob(ctx, layout))
|
|
}
|
|
}
|
|
|
|
/// Infallible conversion of an IR thing to an opaque blob.
|
|
///
|
|
/// The resulting layout is best effort, and is unfortunately not guaranteed to
|
|
/// be correct. When all else fails, we fall back to a single byte layout as a
|
|
/// last resort, because C++ does not permit zero-sized types. See the note in
|
|
/// the `ToRustTyOrOpaque` doc comment about fallible versus infallible traits
|
|
/// and when each is appropriate.
|
|
///
|
|
/// Don't implement this directly. Instead implement `TryToOpaque`, and then
|
|
/// leverage the blanket impl for this trait.
|
|
pub(crate) trait ToOpaque: TryToOpaque {
|
|
fn get_layout(&self, ctx: &BindgenContext, extra: &Self::Extra) -> Layout {
|
|
self.try_get_layout(ctx, extra)
|
|
.unwrap_or_else(|_| Layout::for_size(ctx, 1))
|
|
}
|
|
|
|
fn to_opaque(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
extra: &Self::Extra,
|
|
) -> syn::Type {
|
|
let layout = self.get_layout(ctx, extra);
|
|
helpers::blob(ctx, layout)
|
|
}
|
|
}
|
|
|
|
impl<T> ToOpaque for T where T: TryToOpaque {}
|
|
|
|
/// Fallible conversion from an IR thing to an *equivalent* Rust type.
|
|
///
|
|
/// If the C/C++ construct represented by the IR thing cannot (currently) be
|
|
/// represented in Rust (for example, instantiations of templates with
|
|
/// const-value generic parameters) then the impl should return an `Err`. It
|
|
/// should *not* attempt to return an opaque blob with the correct size and
|
|
/// alignment. That is the responsibility of the `TryToOpaque` trait.
|
|
pub(crate) trait TryToRustTy {
|
|
type Extra;
|
|
|
|
fn try_to_rust_ty(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
extra: &Self::Extra,
|
|
) -> error::Result<syn::Type>;
|
|
}
|
|
|
|
/// Fallible conversion to a Rust type or an opaque blob with the correct size
|
|
/// and alignment.
|
|
///
|
|
/// Don't implement this directly. Instead implement `TryToRustTy` and
|
|
/// `TryToOpaque`, and then leverage the blanket impl for this trait below.
|
|
pub(crate) trait TryToRustTyOrOpaque: TryToRustTy + TryToOpaque {
|
|
type Extra;
|
|
|
|
fn try_to_rust_ty_or_opaque(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
extra: &<Self as TryToRustTyOrOpaque>::Extra,
|
|
) -> error::Result<syn::Type>;
|
|
}
|
|
|
|
impl<E, T> TryToRustTyOrOpaque for T
|
|
where
|
|
T: TryToRustTy<Extra = E> + TryToOpaque<Extra = E>,
|
|
{
|
|
type Extra = E;
|
|
|
|
fn try_to_rust_ty_or_opaque(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
extra: &E,
|
|
) -> error::Result<syn::Type> {
|
|
self.try_to_rust_ty(ctx, extra).or_else(|_| {
|
|
if let Ok(layout) = self.try_get_layout(ctx, extra) {
|
|
Ok(helpers::blob(ctx, layout))
|
|
} else {
|
|
Err(error::Error::NoLayoutForOpaqueBlob)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Infallible conversion to a Rust type, or an opaque blob with a best effort
|
|
/// of correct size and alignment.
|
|
///
|
|
/// Don't implement this directly. Instead implement `TryToRustTy` and
|
|
/// `TryToOpaque`, and then leverage the blanket impl for this trait below.
|
|
///
|
|
/// ### Fallible vs. Infallible Conversions to Rust Types
|
|
///
|
|
/// When should one use this infallible `ToRustTyOrOpaque` trait versus the
|
|
/// fallible `TryTo{RustTy, Opaque, RustTyOrOpaque}` triats? All fallible trait
|
|
/// implementations that need to convert another thing into a Rust type or
|
|
/// opaque blob in a nested manner should also use fallible trait methods and
|
|
/// propagate failure up the stack. Only infallible functions and methods like
|
|
/// CodeGenerator implementations should use the infallible
|
|
/// `ToRustTyOrOpaque`. The further out we push error recovery, the more likely
|
|
/// we are to get a usable `Layout` even if we can't generate an equivalent Rust
|
|
/// type for a C++ construct.
|
|
pub(crate) trait ToRustTyOrOpaque: TryToRustTy + ToOpaque {
|
|
type Extra;
|
|
|
|
fn to_rust_ty_or_opaque(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
extra: &<Self as ToRustTyOrOpaque>::Extra,
|
|
) -> syn::Type;
|
|
}
|
|
|
|
impl<E, T> ToRustTyOrOpaque for T
|
|
where
|
|
T: TryToRustTy<Extra = E> + ToOpaque<Extra = E>,
|
|
{
|
|
type Extra = E;
|
|
|
|
fn to_rust_ty_or_opaque(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
extra: &E,
|
|
) -> syn::Type {
|
|
self.try_to_rust_ty(ctx, extra)
|
|
.unwrap_or_else(|_| self.to_opaque(ctx, extra))
|
|
}
|
|
}
|
|
|
|
impl<T> TryToOpaque for T
|
|
where
|
|
T: Copy + Into<ItemId>,
|
|
{
|
|
type Extra = ();
|
|
|
|
fn try_get_layout(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
_: &(),
|
|
) -> error::Result<Layout> {
|
|
ctx.resolve_item((*self).into()).try_get_layout(ctx, &())
|
|
}
|
|
}
|
|
|
|
impl<T> TryToRustTy for T
|
|
where
|
|
T: Copy + Into<ItemId>,
|
|
{
|
|
type Extra = ();
|
|
|
|
fn try_to_rust_ty(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
_: &(),
|
|
) -> error::Result<syn::Type> {
|
|
ctx.resolve_item((*self).into()).try_to_rust_ty(ctx, &())
|
|
}
|
|
}
|
|
|
|
impl TryToOpaque for Item {
|
|
type Extra = ();
|
|
|
|
fn try_get_layout(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
_: &(),
|
|
) -> error::Result<Layout> {
|
|
self.kind().expect_type().try_get_layout(ctx, self)
|
|
}
|
|
}
|
|
|
|
impl TryToRustTy for Item {
|
|
type Extra = ();
|
|
|
|
fn try_to_rust_ty(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
_: &(),
|
|
) -> error::Result<syn::Type> {
|
|
self.kind().expect_type().try_to_rust_ty(ctx, self)
|
|
}
|
|
}
|
|
|
|
impl TryToOpaque for Type {
|
|
type Extra = Item;
|
|
|
|
fn try_get_layout(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
_: &Item,
|
|
) -> error::Result<Layout> {
|
|
self.layout(ctx).ok_or(error::Error::NoLayoutForOpaqueBlob)
|
|
}
|
|
}
|
|
|
|
impl TryToRustTy for Type {
|
|
type Extra = Item;
|
|
|
|
fn try_to_rust_ty(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
item: &Item,
|
|
) -> error::Result<syn::Type> {
|
|
use self::helpers::ast_ty::*;
|
|
|
|
match *self.kind() {
|
|
TypeKind::Void => Ok(c_void(ctx)),
|
|
// TODO: we should do something smart with nullptr, or maybe *const
|
|
// c_void is enough?
|
|
TypeKind::NullPtr => Ok(c_void(ctx).to_ptr(true)),
|
|
TypeKind::Int(ik) => {
|
|
Ok(int_kind_rust_type(ctx, ik, self.layout(ctx)))
|
|
}
|
|
TypeKind::Float(fk) => {
|
|
Ok(float_kind_rust_type(ctx, fk, self.layout(ctx)))
|
|
}
|
|
TypeKind::Complex(fk) => {
|
|
let float_path =
|
|
float_kind_rust_type(ctx, fk, self.layout(ctx));
|
|
|
|
ctx.generated_bindgen_complex();
|
|
Ok(if ctx.options().enable_cxx_namespaces {
|
|
syn::parse_quote! { root::__BindgenComplex<#float_path> }
|
|
} else {
|
|
syn::parse_quote! { __BindgenComplex<#float_path> }
|
|
})
|
|
}
|
|
TypeKind::Function(ref signature) => {
|
|
// We can't rely on the sizeof(Option<NonZero<_>>) ==
|
|
// sizeof(NonZero<_>) optimization with opaque blobs (because
|
|
// they aren't NonZero), so don't *ever* use an or_opaque
|
|
// variant here.
|
|
let ty = signature.try_to_rust_ty(ctx, item)?;
|
|
|
|
let prefix = ctx.trait_prefix();
|
|
Ok(syn::parse_quote! { ::#prefix::option::Option<#ty> })
|
|
}
|
|
TypeKind::Array(item, len) | TypeKind::Vector(item, len) => {
|
|
let ty = item.try_to_rust_ty(ctx, &())?;
|
|
Ok(syn::parse_quote! { [ #ty ; #len ] })
|
|
}
|
|
TypeKind::Enum(..) => {
|
|
let path = item.namespace_aware_canonical_path(ctx);
|
|
let path = proc_macro2::TokenStream::from_str(&path.join("::"))
|
|
.unwrap();
|
|
Ok(syn::parse_quote!(#path))
|
|
}
|
|
TypeKind::TemplateInstantiation(ref inst) => {
|
|
inst.try_to_rust_ty(ctx, item)
|
|
}
|
|
TypeKind::ResolvedTypeRef(inner) => inner.try_to_rust_ty(ctx, &()),
|
|
TypeKind::TemplateAlias(..) |
|
|
TypeKind::Alias(..) |
|
|
TypeKind::BlockPointer(..) => {
|
|
if self.is_block_pointer() && !ctx.options().generate_block {
|
|
let void = c_void(ctx);
|
|
return Ok(void.to_ptr(/* is_const = */ false));
|
|
}
|
|
|
|
if item.is_opaque(ctx, &()) &&
|
|
item.used_template_params(ctx)
|
|
.into_iter()
|
|
.any(|param| param.is_template_param(ctx, &()))
|
|
{
|
|
self.try_to_opaque(ctx, item)
|
|
} else if let Some(ty) = self
|
|
.name()
|
|
.and_then(|name| utils::type_from_named(ctx, name))
|
|
{
|
|
Ok(ty)
|
|
} else {
|
|
utils::build_path(item, ctx)
|
|
}
|
|
}
|
|
TypeKind::Comp(ref info) => {
|
|
let template_params = item.all_template_params(ctx);
|
|
if info.has_non_type_template_params() ||
|
|
(item.is_opaque(ctx, &()) && !template_params.is_empty())
|
|
{
|
|
return self.try_to_opaque(ctx, item);
|
|
}
|
|
|
|
utils::build_path(item, ctx)
|
|
}
|
|
TypeKind::Opaque => self.try_to_opaque(ctx, item),
|
|
TypeKind::Pointer(inner) | TypeKind::Reference(inner) => {
|
|
let is_const = ctx.resolve_type(inner).is_const();
|
|
|
|
let inner =
|
|
inner.into_resolver().through_type_refs().resolve(ctx);
|
|
let inner_ty = inner.expect_type();
|
|
|
|
let is_objc_pointer =
|
|
matches!(inner_ty.kind(), TypeKind::ObjCInterface(..));
|
|
|
|
// Regardless if we can properly represent the inner type, we
|
|
// should always generate a proper pointer here, so use
|
|
// infallible conversion of the inner type.
|
|
let ty = inner
|
|
.to_rust_ty_or_opaque(ctx, &())
|
|
.with_implicit_template_params(ctx, inner);
|
|
|
|
// Avoid the first function pointer level, since it's already
|
|
// represented in Rust.
|
|
if inner_ty.canonical_type(ctx).is_function() || is_objc_pointer
|
|
{
|
|
Ok(ty)
|
|
} else {
|
|
Ok(ty.to_ptr(is_const))
|
|
}
|
|
}
|
|
TypeKind::TypeParam => {
|
|
let name = item.canonical_name(ctx);
|
|
let ident = ctx.rust_ident(name);
|
|
Ok(syn::parse_quote! { #ident })
|
|
}
|
|
TypeKind::ObjCSel => Ok(syn::parse_quote! { objc::runtime::Sel }),
|
|
TypeKind::ObjCId => Ok(syn::parse_quote! { id }),
|
|
TypeKind::ObjCInterface(ref interface) => {
|
|
let name = ctx.rust_ident(interface.name());
|
|
Ok(syn::parse_quote! { #name })
|
|
}
|
|
ref u @ TypeKind::UnresolvedTypeRef(..) => {
|
|
unreachable!("Should have been resolved after parsing {:?}!", u)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryToOpaque for TemplateInstantiation {
|
|
type Extra = Item;
|
|
|
|
fn try_get_layout(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
item: &Item,
|
|
) -> error::Result<Layout> {
|
|
item.expect_type()
|
|
.layout(ctx)
|
|
.ok_or(error::Error::NoLayoutForOpaqueBlob)
|
|
}
|
|
}
|
|
|
|
impl TryToRustTy for TemplateInstantiation {
|
|
type Extra = Item;
|
|
|
|
fn try_to_rust_ty(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
item: &Item,
|
|
) -> error::Result<syn::Type> {
|
|
if self.is_opaque(ctx, item) {
|
|
return Err(error::Error::InstantiationOfOpaqueType);
|
|
}
|
|
|
|
let def = self
|
|
.template_definition()
|
|
.into_resolver()
|
|
.through_type_refs()
|
|
.resolve(ctx);
|
|
|
|
let mut ty = quote! {};
|
|
let def_path = def.namespace_aware_canonical_path(ctx);
|
|
ty.append_separated(
|
|
def_path.into_iter().map(|p| ctx.rust_ident(p)),
|
|
quote!(::),
|
|
);
|
|
|
|
let def_params = def.self_template_params(ctx);
|
|
if def_params.is_empty() {
|
|
// This can happen if we generated an opaque type for a partial
|
|
// template specialization, and we've hit an instantiation of
|
|
// that partial specialization.
|
|
extra_assert!(def.is_opaque(ctx, &()));
|
|
return Err(error::Error::InstantiationOfOpaqueType);
|
|
}
|
|
|
|
// TODO: If the definition type is a template class/struct
|
|
// definition's member template definition, it could rely on
|
|
// generic template parameters from its outer template
|
|
// class/struct. When we emit bindings for it, it could require
|
|
// *more* type arguments than we have here, and we will need to
|
|
// reconstruct them somehow. We don't have any means of doing
|
|
// that reconstruction at this time.
|
|
|
|
let template_args = self
|
|
.template_arguments()
|
|
.iter()
|
|
.zip(def_params.iter())
|
|
// Only pass type arguments for the type parameters that
|
|
// the def uses.
|
|
.filter(|&(_, param)| ctx.uses_template_parameter(def.id(), *param))
|
|
.map(|(arg, _)| {
|
|
let arg = arg.into_resolver().through_type_refs().resolve(ctx);
|
|
let ty = arg
|
|
.try_to_rust_ty(ctx, &())?
|
|
.with_implicit_template_params(ctx, arg);
|
|
Ok(ty)
|
|
})
|
|
.collect::<error::Result<Vec<_>>>()?;
|
|
|
|
Ok(if template_args.is_empty() {
|
|
syn::parse_quote! { #ty }
|
|
} else {
|
|
syn::parse_quote! { #ty<#(#template_args),*> }
|
|
})
|
|
}
|
|
}
|
|
|
|
impl TryToRustTy for FunctionSig {
|
|
type Extra = Item;
|
|
|
|
fn try_to_rust_ty(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
item: &Item,
|
|
) -> error::Result<syn::Type> {
|
|
// TODO: we might want to consider ignoring the reference return value.
|
|
let ret = utils::fnsig_return_ty(ctx, self);
|
|
let arguments = utils::fnsig_arguments(ctx, self);
|
|
|
|
match self.abi(ctx, None) {
|
|
Ok(abi) => Ok(
|
|
syn::parse_quote! { unsafe extern #abi fn ( #( #arguments ),* ) #ret },
|
|
),
|
|
Err(err) => {
|
|
if matches!(err, error::Error::UnsupportedAbi(_)) {
|
|
unsupported_abi_diagnostic(
|
|
self.name(),
|
|
self.is_variadic(),
|
|
item.location(),
|
|
ctx,
|
|
&err,
|
|
);
|
|
}
|
|
|
|
Err(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CodeGenerator for Function {
|
|
type Extra = Item;
|
|
|
|
/// If we've actually generated the symbol, the number of times we've seen
|
|
/// it.
|
|
type Return = Option<u32>;
|
|
|
|
fn codegen(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
result: &mut CodegenResult<'_>,
|
|
item: &Item,
|
|
) -> Self::Return {
|
|
debug!("<Function as CodeGenerator>::codegen: item = {:?}", item);
|
|
debug_assert!(item.is_enabled_for_codegen(ctx));
|
|
|
|
let is_internal = matches!(self.linkage(), Linkage::Internal);
|
|
|
|
let signature_item = ctx.resolve_item(self.signature());
|
|
let signature = signature_item.kind().expect_type().canonical_type(ctx);
|
|
let signature = match *signature.kind() {
|
|
TypeKind::Function(ref sig) => sig,
|
|
_ => panic!("Signature kind is not a Function: {:?}", signature),
|
|
};
|
|
|
|
if is_internal {
|
|
if !ctx.options().wrap_static_fns {
|
|
// We cannot do anything with internal functions if we are not wrapping them so
|
|
// just avoid generating anything for them.
|
|
return None;
|
|
}
|
|
|
|
if signature.is_variadic() {
|
|
// We cannot generate wrappers for variadic static functions so we avoid
|
|
// generating any code for them.
|
|
variadic_fn_diagnostic(self.name(), item.location(), ctx);
|
|
return None;
|
|
}
|
|
}
|
|
|
|
// Pure virtual methods have no actual symbol, so we can't generate
|
|
// something meaningful for them.
|
|
let is_dynamic_function = match self.kind() {
|
|
FunctionKind::Method(ref method_kind)
|
|
if method_kind.is_pure_virtual() =>
|
|
{
|
|
return None;
|
|
}
|
|
FunctionKind::Function => {
|
|
ctx.options().dynamic_library_name.is_some()
|
|
}
|
|
_ => false,
|
|
};
|
|
|
|
// Similar to static member variables in a class template, we can't
|
|
// generate bindings to template functions, because the set of
|
|
// instantiations is open ended and we have no way of knowing which
|
|
// monomorphizations actually exist.
|
|
if !item.all_template_params(ctx).is_empty() {
|
|
return None;
|
|
}
|
|
|
|
let name = self.name();
|
|
let mut canonical_name = item.canonical_name(ctx);
|
|
let mangled_name = self.mangled_name();
|
|
|
|
{
|
|
let seen_symbol_name = mangled_name.unwrap_or(&canonical_name);
|
|
|
|
// TODO: Maybe warn here if there's a type/argument mismatch, or
|
|
// something?
|
|
if result.seen_function(seen_symbol_name) {
|
|
return None;
|
|
}
|
|
result.saw_function(seen_symbol_name);
|
|
}
|
|
|
|
let mut attributes = vec![];
|
|
|
|
if ctx.options().rust_features().must_use_function {
|
|
let must_use = signature.must_use() || {
|
|
let ret_ty = signature
|
|
.return_type()
|
|
.into_resolver()
|
|
.through_type_refs()
|
|
.resolve(ctx);
|
|
ret_ty.must_use(ctx)
|
|
};
|
|
|
|
if must_use {
|
|
attributes.push(attributes::must_use());
|
|
}
|
|
}
|
|
|
|
if let Some(comment) = item.comment(ctx) {
|
|
attributes.push(attributes::doc(comment));
|
|
}
|
|
|
|
let abi = match signature.abi(ctx, Some(name)) {
|
|
Err(err) => {
|
|
if matches!(err, error::Error::UnsupportedAbi(_)) {
|
|
unsupported_abi_diagnostic(
|
|
name,
|
|
signature.is_variadic(),
|
|
item.location(),
|
|
ctx,
|
|
&err,
|
|
);
|
|
}
|
|
|
|
return None;
|
|
}
|
|
Ok(ClangAbi::Unknown(unknown_abi)) => {
|
|
panic!(
|
|
"Invalid or unknown abi {:?} for function {:?} ({:?})",
|
|
unknown_abi, canonical_name, self
|
|
);
|
|
}
|
|
Ok(abi) => abi,
|
|
};
|
|
|
|
// Handle overloaded functions by giving each overload its own unique
|
|
// suffix.
|
|
let times_seen = result.overload_number(&canonical_name);
|
|
if times_seen > 0 {
|
|
write!(&mut canonical_name, "{}", times_seen).unwrap();
|
|
}
|
|
|
|
let mut has_link_name_attr = false;
|
|
if let Some(link_name) = self.link_name() {
|
|
attributes.push(attributes::link_name::<false>(link_name));
|
|
has_link_name_attr = true;
|
|
} else {
|
|
let link_name = mangled_name.unwrap_or(name);
|
|
if !is_dynamic_function &&
|
|
!utils::names_will_be_identical_after_mangling(
|
|
&canonical_name,
|
|
link_name,
|
|
Some(abi),
|
|
)
|
|
{
|
|
attributes.push(attributes::link_name::<false>(link_name));
|
|
has_link_name_attr = true;
|
|
}
|
|
}
|
|
|
|
// Unfortunately this can't piggyback on the `attributes` list because
|
|
// the #[link(wasm_import_module)] needs to happen before the `extern
|
|
// "C"` block. It doesn't get picked up properly otherwise
|
|
let wasm_link_attribute =
|
|
ctx.options().wasm_import_module_name.as_ref().map(|name| {
|
|
quote! { #[link(wasm_import_module = #name)] }
|
|
});
|
|
|
|
let should_wrap =
|
|
is_internal && ctx.options().wrap_static_fns && !has_link_name_attr;
|
|
|
|
if should_wrap {
|
|
let name = canonical_name.clone() + ctx.wrap_static_fns_suffix();
|
|
attributes.push(attributes::link_name::<true>(&name));
|
|
}
|
|
|
|
let wrap_as_variadic = if should_wrap && !signature.is_variadic() {
|
|
utils::wrap_as_variadic_fn(ctx, signature, name)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let (ident, args) = if let Some(WrapAsVariadic {
|
|
idx_of_va_list_arg,
|
|
new_name,
|
|
}) = &wrap_as_variadic
|
|
{
|
|
(
|
|
new_name,
|
|
utils::fnsig_arguments_iter(
|
|
ctx,
|
|
// Prune argument at index (idx_of_va_list_arg)
|
|
signature.argument_types().iter().enumerate().filter_map(
|
|
|(idx, t)| {
|
|
if idx == *idx_of_va_list_arg {
|
|
None
|
|
} else {
|
|
Some(t)
|
|
}
|
|
},
|
|
),
|
|
// and replace it by a `...` (variadic symbol and the end of the signature)
|
|
true,
|
|
),
|
|
)
|
|
} else {
|
|
(&canonical_name, utils::fnsig_arguments(ctx, signature))
|
|
};
|
|
let ret = utils::fnsig_return_ty(ctx, signature);
|
|
|
|
let ident = ctx.rust_ident(ident);
|
|
let tokens = quote! {
|
|
#wasm_link_attribute
|
|
extern #abi {
|
|
#(#attributes)*
|
|
pub fn #ident ( #( #args ),* ) #ret;
|
|
}
|
|
};
|
|
|
|
// Add the item to the serialization list if necessary
|
|
if should_wrap {
|
|
result
|
|
.items_to_serialize
|
|
.push((item.id(), wrap_as_variadic));
|
|
}
|
|
|
|
// If we're doing dynamic binding generation, add to the dynamic items.
|
|
if is_dynamic_function {
|
|
let args_identifiers =
|
|
utils::fnsig_argument_identifiers(ctx, signature);
|
|
let ret_ty = utils::fnsig_return_ty(ctx, signature);
|
|
result.dynamic_items().push(
|
|
ident,
|
|
abi,
|
|
signature.is_variadic(),
|
|
ctx.options().dynamic_link_require_all,
|
|
args,
|
|
args_identifiers,
|
|
ret,
|
|
ret_ty,
|
|
attributes,
|
|
ctx,
|
|
);
|
|
} else {
|
|
result.push(tokens);
|
|
}
|
|
Some(times_seen)
|
|
}
|
|
}
|
|
|
|
#[cfg_attr(not(feature = "experimental"), allow(unused_variables))]
|
|
fn unsupported_abi_diagnostic(
|
|
fn_name: &str,
|
|
variadic: bool,
|
|
location: Option<&crate::clang::SourceLocation>,
|
|
ctx: &BindgenContext,
|
|
error: &error::Error,
|
|
) {
|
|
warn!(
|
|
"Skipping {}function `{}` because the {}",
|
|
if variadic { "variadic " } else { "" },
|
|
fn_name,
|
|
error
|
|
);
|
|
|
|
#[cfg(feature = "experimental")]
|
|
if ctx.options().emit_diagnostics {
|
|
use crate::diagnostics::{get_line, Diagnostic, Level, Slice};
|
|
|
|
let mut diag = Diagnostic::default();
|
|
diag.with_title(
|
|
format!(
|
|
"Skipping {}function `{}` because the {}",
|
|
if variadic { "variadic " } else { "" },
|
|
fn_name,
|
|
error
|
|
),
|
|
Level::Warn,
|
|
)
|
|
.add_annotation(
|
|
"No code will be generated for this function.",
|
|
Level::Warn,
|
|
)
|
|
.add_annotation(
|
|
format!(
|
|
"The configured Rust version is {}.",
|
|
ctx.options().rust_target
|
|
),
|
|
Level::Note,
|
|
);
|
|
|
|
if let Some(loc) = location {
|
|
let (file, line, col, _) = loc.location();
|
|
|
|
if let Some(filename) = file.name() {
|
|
if let Ok(Some(source)) = get_line(&filename, line) {
|
|
let mut slice = Slice::default();
|
|
slice
|
|
.with_source(source)
|
|
.with_location(filename, line, col);
|
|
diag.add_slice(slice);
|
|
}
|
|
}
|
|
}
|
|
|
|
diag.display()
|
|
}
|
|
}
|
|
|
|
fn variadic_fn_diagnostic(
|
|
fn_name: &str,
|
|
_location: Option<&crate::clang::SourceLocation>,
|
|
_ctx: &BindgenContext,
|
|
) {
|
|
warn!(
|
|
"Cannot generate wrapper for the static variadic function `{}`.",
|
|
fn_name,
|
|
);
|
|
|
|
#[cfg(feature = "experimental")]
|
|
if _ctx.options().emit_diagnostics {
|
|
use crate::diagnostics::{get_line, Diagnostic, Level, Slice};
|
|
|
|
let mut diag = Diagnostic::default();
|
|
|
|
diag.with_title(format!("Cannot generate wrapper for the static function `{}`.", fn_name), Level::Warn)
|
|
.add_annotation("The `--wrap-static-fns` feature does not support variadic functions.", Level::Note)
|
|
.add_annotation("No code will be generated for this function.", Level::Note);
|
|
|
|
if let Some(loc) = _location {
|
|
let (file, line, col, _) = loc.location();
|
|
|
|
if let Some(filename) = file.name() {
|
|
if let Ok(Some(source)) = get_line(&filename, line) {
|
|
let mut slice = Slice::default();
|
|
slice
|
|
.with_source(source)
|
|
.with_location(filename, line, col);
|
|
diag.add_slice(slice);
|
|
}
|
|
}
|
|
}
|
|
|
|
diag.display()
|
|
}
|
|
}
|
|
|
|
fn objc_method_codegen(
|
|
ctx: &BindgenContext,
|
|
method: &ObjCMethod,
|
|
methods: &mut Vec<proc_macro2::TokenStream>,
|
|
class_name: Option<&str>,
|
|
rust_class_name: &str,
|
|
prefix: &str,
|
|
) {
|
|
// This would ideally resolve the method into an Item, and use
|
|
// Item::process_before_codegen; however, ObjC methods are not currently
|
|
// made into function items.
|
|
let name = format!("{}::{}{}", rust_class_name, prefix, method.rust_name());
|
|
if ctx.options().blocklisted_items.matches(name) {
|
|
return;
|
|
}
|
|
|
|
let signature = method.signature();
|
|
let fn_args = utils::fnsig_arguments(ctx, signature);
|
|
let fn_ret = utils::fnsig_return_ty(ctx, signature);
|
|
|
|
let sig = if method.is_class_method() {
|
|
quote! {
|
|
( #( #fn_args ),* ) #fn_ret
|
|
}
|
|
} else {
|
|
let self_arr = [quote! { &self }];
|
|
let args = self_arr.iter().chain(fn_args.iter());
|
|
quote! {
|
|
( #( #args ),* ) #fn_ret
|
|
}
|
|
};
|
|
|
|
let methods_and_args = method.format_method_call(&fn_args);
|
|
|
|
let body = {
|
|
let body = if method.is_class_method() {
|
|
let class_name = ctx.rust_ident(
|
|
class_name
|
|
.expect("Generating a class method without class name?"),
|
|
);
|
|
quote!(msg_send!(class!(#class_name), #methods_and_args))
|
|
} else {
|
|
quote!(msg_send!(*self, #methods_and_args))
|
|
};
|
|
|
|
ctx.wrap_unsafe_ops(body)
|
|
};
|
|
|
|
let method_name =
|
|
ctx.rust_ident(format!("{}{}", prefix, method.rust_name()));
|
|
|
|
methods.push(quote! {
|
|
unsafe fn #method_name #sig where <Self as std::ops::Deref>::Target: objc::Message + Sized {
|
|
#body
|
|
}
|
|
});
|
|
}
|
|
|
|
impl CodeGenerator for ObjCInterface {
|
|
type Extra = Item;
|
|
type Return = ();
|
|
|
|
fn codegen(
|
|
&self,
|
|
ctx: &BindgenContext,
|
|
result: &mut CodegenResult<'_>,
|
|
item: &Item,
|
|
) {
|
|
debug_assert!(item.is_enabled_for_codegen(ctx));
|
|
|
|
let mut impl_items = vec![];
|
|
let rust_class_name = item.path_for_allowlisting(ctx)[1..].join("::");
|
|
|
|
for method in self.methods() {
|
|
objc_method_codegen(
|
|
ctx,
|
|
method,
|
|
&mut impl_items,
|
|
None,
|
|
&rust_class_name,
|
|
"",
|
|
);
|
|
}
|
|
|
|
for class_method in self.class_methods() {
|
|
let ambiquity = self
|
|
.methods()
|
|
.iter()
|
|
.map(|m| m.rust_name())
|
|
.any(|x| x == class_method.rust_name());
|
|
let prefix = if ambiquity { "class_" } else { "" };
|
|
objc_method_codegen(
|
|
ctx,
|
|
class_method,
|
|
&mut impl_items,
|
|
Some(self.name()),
|
|
&rust_class_name,
|
|
prefix,
|
|
);
|
|
}
|
|
|
|
let trait_name = ctx.rust_ident(self.rust_name());
|
|
let trait_constraints = quote! {
|
|
Sized + std::ops::Deref
|
|
};
|
|
let trait_block = if self.is_template() {
|
|
let template_names: Vec<Ident> = self
|
|
.template_names
|
|
.iter()
|
|
.map(|g| ctx.rust_ident(g))
|
|
.collect();
|
|
|
|
quote! {
|
|
pub trait #trait_name <#(#template_names:'static),*> : #trait_constraints {
|
|
#( #impl_items )*
|
|
}
|
|
}
|
|
} else {
|
|
quote! {
|
|
pub trait #trait_name : #trait_constraints {
|
|
#( #impl_items )*
|
|
}
|
|
}
|
|
};
|
|
|
|
let class_name = ctx.rust_ident(self.name());
|
|
if !self.is_category() && !self.is_protocol() {
|
|
let struct_block = quote! {
|
|
#[repr(transparent)]
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub struct #class_name(pub id);
|
|
impl std::ops::Deref for #class_name {
|
|
type Target = objc::runtime::Object;
|
|
fn deref(&self) -> &Self::Target {
|
|
unsafe {
|
|
&*self.0
|
|
}
|
|
}
|
|
}
|
|
unsafe impl objc::Message for #class_name { }
|
|
impl #class_name {
|
|
pub fn alloc() -> Self {
|
|
Self(unsafe {
|
|
msg_send!(class!(#class_name), alloc)
|
|
})
|
|
}
|
|
}
|
|
};
|
|
result.push(struct_block);
|
|
let mut protocol_set: HashSet<ItemId> = Default::default();
|
|
for protocol_id in self.conforms_to.iter() {
|
|
protocol_set.insert(*protocol_id);
|
|
let protocol_name = ctx.rust_ident(
|
|
ctx.resolve_type(protocol_id.expect_type_id(ctx))
|
|
.name()
|
|
.unwrap(),
|
|
);
|
|
let impl_trait = quote! {
|
|
impl #protocol_name for #class_name { }
|
|
};
|
|
result.push(impl_trait);
|
|
}
|
|
let mut parent_class = self.parent_class;
|
|
while let Some(parent_id) = parent_class {
|
|
let parent = parent_id
|
|
.expect_type_id(ctx)
|
|
.into_resolver()
|
|
.through_type_refs()
|
|
.resolve(ctx)
|
|
.expect_type()
|
|
.kind();
|
|
|
|
let parent = match parent {
|
|
TypeKind::ObjCInterface(ref parent) => parent,
|
|
_ => break,
|
|
};
|
|
parent_class = parent.parent_class;
|
|
|
|
let parent_name = ctx.rust_ident(parent.rust_name());
|
|
let impl_trait = if parent.is_template() {
|
|
let template_names: Vec<Ident> = parent
|
|
.template_names
|
|
.iter()
|
|
.map(|g| ctx.rust_ident(g))
|
|
.collect();
|
|
quote! {
|
|
impl <#(#template_names :'static),*> #parent_name <#(#template_names),*> for #class_name {
|
|
}
|
|
}
|
|
} else {
|
|
quote! {
|
|
impl #parent_name for #class_name { }
|
|
}
|
|
};
|
|
result.push(impl_trait);
|
|
for protocol_id in parent.conforms_to.iter() {
|
|
if protocol_set.insert(*protocol_id) {
|
|
let protocol_name = ctx.rust_ident(
|
|
ctx.resolve_type(protocol_id.expect_type_id(ctx))
|
|
.name()
|
|
.unwrap(),
|
|
);
|
|
let impl_trait = quote! {
|
|
impl #protocol_name for #class_name { }
|
|
};
|
|
result.push(impl_trait);
|
|
}
|
|
}
|
|
if !parent.is_template() {
|
|
let parent_struct_name = parent.name();
|
|
let child_struct_name = self.name();
|
|
let parent_struct = ctx.rust_ident(parent_struct_name);
|
|
let from_block = quote! {
|
|
impl From<#class_name> for #parent_struct {
|
|
fn from(child: #class_name) -> #parent_struct {
|
|
#parent_struct(child.0)
|
|
}
|
|
}
|
|
};
|
|
result.push(from_block);
|
|
|
|
let error_msg = format!(
|
|
"This {} cannot be downcasted to {}",
|
|
parent_struct_name, child_struct_name
|
|
);
|
|
let try_into_block = quote! {
|
|
impl std::convert::TryFrom<#parent_struct> for #class_name {
|
|
type Error = &'static str;
|
|
fn try_from(parent: #parent_struct) -> Result<#class_name, Self::Error> {
|
|
let is_kind_of : bool = unsafe { msg_send!(parent, isKindOfClass:class!(#class_name))};
|
|
if is_kind_of {
|
|
Ok(#class_name(parent.0))
|
|
} else {
|
|
Err(#error_msg)
|
|
}
|
|
}
|
|
}
|
|
};
|
|
result.push(try_into_block);
|
|
}
|
|
}
|
|
}
|
|
|
|
if !self.is_protocol() {
|
|
let impl_block = if self.is_template() {
|
|
let template_names: Vec<Ident> = self
|
|
.template_names
|
|
.iter()
|
|
.map(|g| ctx.rust_ident(g))
|
|
.collect();
|
|
quote! {
|
|
impl <#(#template_names :'static),*> #trait_name <#(#template_names),*> for #class_name {
|
|
}
|
|
}
|
|
} else {
|
|
quote! {
|
|
impl #trait_name for #class_name {
|
|
}
|
|
}
|
|
};
|
|
result.push(impl_block);
|
|
}
|
|
|
|
result.push(trait_block);
|
|
result.saw_objc();
|
|
}
|
|
}
|
|
|
|
pub(crate) fn codegen(
|
|
context: BindgenContext,
|
|
) -> Result<(proc_macro2::TokenStream, BindgenOptions), CodegenError> {
|
|
context.gen(|context| {
|
|
let _t = context.timer("codegen");
|
|
let counter = Cell::new(0);
|
|
let mut result = CodegenResult::new(&counter);
|
|
|
|
debug!("codegen: {:?}", context.options());
|
|
|
|
if context.options().emit_ir {
|
|
let codegen_items = context.codegen_items();
|
|
for (id, item) in context.items() {
|
|
if codegen_items.contains(&id) {
|
|
println!("ir: {:?} = {:#?}", id, item);
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Some(path) = context.options().emit_ir_graphviz.as_ref() {
|
|
match dot::write_dot_file(context, path) {
|
|
Ok(()) => info!(
|
|
"Your dot file was generated successfully into: {}",
|
|
path
|
|
),
|
|
Err(e) => warn!("{}", e),
|
|
}
|
|
}
|
|
|
|
if let Some(spec) = context.options().depfile.as_ref() {
|
|
match spec.write(context.deps()) {
|
|
Ok(()) => info!(
|
|
"Your depfile was generated successfully into: {}",
|
|
spec.depfile_path.display()
|
|
),
|
|
Err(e) => warn!("{}", e),
|
|
}
|
|
}
|
|
|
|
context.resolve_item(context.root_module()).codegen(
|
|
context,
|
|
&mut result,
|
|
&(),
|
|
);
|
|
|
|
if let Some(ref lib_name) = context.options().dynamic_library_name {
|
|
let lib_ident = context.rust_ident(lib_name);
|
|
let dynamic_items_tokens =
|
|
result.dynamic_items().get_tokens(lib_ident, context);
|
|
result.push(dynamic_items_tokens);
|
|
}
|
|
|
|
utils::serialize_items(&result, context)?;
|
|
|
|
Ok(postprocessing::postprocessing(
|
|
result.items,
|
|
context.options(),
|
|
))
|
|
})
|
|
}
|
|
|
|
pub(crate) mod utils {
|
|
use super::serialize::CSerialize;
|
|
use super::{error, CodegenError, CodegenResult, ToRustTyOrOpaque};
|
|
use crate::ir::context::BindgenContext;
|
|
use crate::ir::context::TypeId;
|
|
use crate::ir::function::{Abi, ClangAbi, FunctionSig};
|
|
use crate::ir::item::{Item, ItemCanonicalPath};
|
|
use crate::ir::ty::TypeKind;
|
|
use crate::{args_are_cpp, file_is_cpp};
|
|
use std::borrow::Cow;
|
|
use std::io::Write;
|
|
use std::mem;
|
|
use std::path::PathBuf;
|
|
use std::str::FromStr;
|
|
|
|
pub(super) fn serialize_items(
|
|
result: &CodegenResult,
|
|
context: &BindgenContext,
|
|
) -> Result<(), CodegenError> {
|
|
if result.items_to_serialize.is_empty() {
|
|
return Ok(());
|
|
}
|
|
|
|
let path = context
|
|
.options()
|
|
.wrap_static_fns_path
|
|
.as_ref()
|
|
.map(PathBuf::from)
|
|
.unwrap_or_else(|| {
|
|
std::env::temp_dir().join("bindgen").join("extern")
|
|
});
|
|
|
|
let dir = path.parent().unwrap();
|
|
|
|
if !dir.exists() {
|
|
std::fs::create_dir_all(dir)?;
|
|
}
|
|
|
|
let is_cpp = args_are_cpp(&context.options().clang_args) ||
|
|
context
|
|
.options()
|
|
.input_headers
|
|
.iter()
|
|
.any(|h| file_is_cpp(h));
|
|
|
|
let source_path = path.with_extension(if is_cpp { "cpp" } else { "c" });
|
|
|
|
let mut code = Vec::new();
|
|
|
|
if !context.options().input_headers.is_empty() {
|
|
for header in &context.options().input_headers {
|
|
writeln!(code, "#include \"{}\"", header)?;
|
|
}
|
|
|
|
writeln!(code)?;
|
|
}
|
|
|
|
if !context.options().input_header_contents.is_empty() {
|
|
for (name, contents) in &context.options().input_header_contents {
|
|
writeln!(code, "// {}\n{}", name, contents)?;
|
|
}
|
|
|
|
writeln!(code)?;
|
|
}
|
|
|
|
writeln!(code, "// Static wrappers\n")?;
|
|
|
|
for (id, wrap_as_variadic) in &result.items_to_serialize {
|
|
let item = context.resolve_item(*id);
|
|
item.serialize(context, wrap_as_variadic, &mut vec![], &mut code)?;
|
|
}
|
|
|
|
std::fs::write(source_path, code)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub(super) fn wrap_as_variadic_fn(
|
|
ctx: &BindgenContext,
|
|
signature: &FunctionSig,
|
|
name: &str,
|
|
) -> Option<super::WrapAsVariadic> {
|
|
// Fast path, exclude because:
|
|
// - with 0 args: no va_list possible, so no point searching for one
|
|
// - with 1 args: cannot have a `va_list` and another arg (required by va_start)
|
|
if signature.argument_types().len() <= 1 {
|
|
return None;
|
|
}
|
|
|
|
let mut it = signature.argument_types().iter().enumerate().filter_map(
|
|
|(idx, (_name, mut type_id))| {
|
|
// Hand rolled visitor that checks for the presence of `va_list`
|
|
loop {
|
|
let ty = ctx.resolve_type(type_id);
|
|
if Some("__builtin_va_list") == ty.name() {
|
|
return Some(idx);
|
|
}
|
|
match ty.kind() {
|
|
TypeKind::Alias(type_id_alias) => {
|
|
type_id = *type_id_alias
|
|
}
|
|
TypeKind::ResolvedTypeRef(type_id_typedef) => {
|
|
type_id = *type_id_typedef
|
|
}
|
|
_ => break,
|
|
}
|
|
}
|
|
None
|
|
},
|
|
);
|
|
|
|
// Return THE idx (by checking that there is no idx after)
|
|
// This is done since we cannot handle multiple `va_list`
|
|
it.next().filter(|_| it.next().is_none()).and_then(|idx| {
|
|
// Call the `wrap_as_variadic_fn` callback
|
|
#[cfg(feature = "experimental")]
|
|
{
|
|
ctx.options()
|
|
.last_callback(|c| c.wrap_as_variadic_fn(name))
|
|
.map(|new_name| super::WrapAsVariadic {
|
|
new_name,
|
|
idx_of_va_list_arg: idx,
|
|
})
|
|
}
|
|
#[cfg(not(feature = "experimental"))]
|
|
{
|
|
let _ = name;
|
|
let _ = idx;
|
|
None
|
|
}
|
|
})
|
|
}
|
|
|
|
pub(crate) fn prepend_bitfield_unit_type(
|
|
ctx: &BindgenContext,
|
|
result: &mut Vec<proc_macro2::TokenStream>,
|
|
) {
|
|
let bitfield_unit_src = include_str!("./bitfield_unit.rs");
|
|
let bitfield_unit_src = if ctx.options().rust_features().min_const_fn {
|
|
Cow::Borrowed(bitfield_unit_src)
|
|
} else {
|
|
Cow::Owned(bitfield_unit_src.replace("const fn ", "fn "))
|
|
};
|
|
let bitfield_unit_type =
|
|
proc_macro2::TokenStream::from_str(&bitfield_unit_src).unwrap();
|
|
let bitfield_unit_type = quote!(#bitfield_unit_type);
|
|
|
|
let items = vec![bitfield_unit_type];
|
|
let old_items = mem::replace(result, items);
|
|
result.extend(old_items);
|
|
}
|
|
|
|
pub(crate) fn prepend_objc_header(
|
|
ctx: &BindgenContext,
|
|
result: &mut Vec<proc_macro2::TokenStream>,
|
|
) {
|
|
let use_objc = if ctx.options().objc_extern_crate {
|
|
quote! {
|
|
#[macro_use]
|
|
extern crate objc;
|
|
}
|
|
} else {
|
|
quote! {
|
|
use objc::{self, msg_send, sel, sel_impl, class};
|
|
}
|
|
};
|
|
|
|
let id_type = quote! {
|
|
#[allow(non_camel_case_types)]
|
|
pub type id = *mut objc::runtime::Object;
|
|
};
|
|
|
|
let items = vec![use_objc, id_type];
|
|
let old_items = mem::replace(result, items);
|
|
result.extend(old_items);
|
|
}
|
|
|
|
pub(crate) fn prepend_block_header(
|
|
ctx: &BindgenContext,
|
|
result: &mut Vec<proc_macro2::TokenStream>,
|
|
) {
|
|
let use_block = if ctx.options().block_extern_crate {
|
|
quote! {
|
|
extern crate block;
|
|
}
|
|
} else {
|
|
quote! {
|
|
use block;
|
|
}
|
|
};
|
|
|
|
let items = vec![use_block];
|
|
let old_items = mem::replace(result, items);
|
|
result.extend(old_items);
|
|
}
|
|
|
|
pub(crate) fn prepend_union_types(
|
|
ctx: &BindgenContext,
|
|
result: &mut Vec<proc_macro2::TokenStream>,
|
|
) {
|
|
let prefix = ctx.trait_prefix();
|
|
|
|
// If the target supports `const fn`, declare eligible functions
|
|
// as `const fn` else just `fn`.
|
|
let const_fn = if ctx.options().rust_features().min_const_fn {
|
|
quote! { const fn }
|
|
} else {
|
|
quote! { fn }
|
|
};
|
|
|
|
// TODO(emilio): The fmt::Debug impl could be way nicer with
|
|
// std::intrinsics::type_name, but...
|
|
let union_field_decl = quote! {
|
|
#[repr(C)]
|
|
pub struct __BindgenUnionField<T>(::#prefix::marker::PhantomData<T>);
|
|
};
|
|
|
|
let transmute =
|
|
ctx.wrap_unsafe_ops(quote!(::#prefix::mem::transmute(self)));
|
|
|
|
let union_field_impl = quote! {
|
|
impl<T> __BindgenUnionField<T> {
|
|
#[inline]
|
|
pub #const_fn new() -> Self {
|
|
__BindgenUnionField(::#prefix::marker::PhantomData)
|
|
}
|
|
|
|
#[inline]
|
|
pub unsafe fn as_ref(&self) -> &T {
|
|
#transmute
|
|
}
|
|
|
|
#[inline]
|
|
pub unsafe fn as_mut(&mut self) -> &mut T {
|
|
#transmute
|
|
}
|
|
}
|
|
};
|
|
|
|
let union_field_default_impl = quote! {
|
|
impl<T> ::#prefix::default::Default for __BindgenUnionField<T> {
|
|
#[inline]
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
};
|
|
|
|
let union_field_clone_impl = quote! {
|
|
impl<T> ::#prefix::clone::Clone for __BindgenUnionField<T> {
|
|
#[inline]
|
|
fn clone(&self) -> Self {
|
|
*self
|
|
}
|
|
}
|
|
};
|
|
|
|
let union_field_copy_impl = quote! {
|
|
impl<T> ::#prefix::marker::Copy for __BindgenUnionField<T> {}
|
|
};
|
|
|
|
let union_field_debug_impl = quote! {
|
|
impl<T> ::#prefix::fmt::Debug for __BindgenUnionField<T> {
|
|
fn fmt(&self, fmt: &mut ::#prefix::fmt::Formatter<'_>)
|
|
-> ::#prefix::fmt::Result {
|
|
fmt.write_str("__BindgenUnionField")
|
|
}
|
|
}
|
|
};
|
|
|
|
// The actual memory of the filed will be hashed, so that's why these
|
|
// field doesn't do anything with the hash.
|
|
let union_field_hash_impl = quote! {
|
|
impl<T> ::#prefix::hash::Hash for __BindgenUnionField<T> {
|
|
fn hash<H: ::#prefix::hash::Hasher>(&self, _state: &mut H) {
|
|
}
|
|
}
|
|
};
|
|
|
|
let union_field_partialeq_impl = quote! {
|
|
impl<T> ::#prefix::cmp::PartialEq for __BindgenUnionField<T> {
|
|
fn eq(&self, _other: &__BindgenUnionField<T>) -> bool {
|
|
true
|
|
}
|
|
}
|
|
};
|
|
|
|
let union_field_eq_impl = quote! {
|
|
impl<T> ::#prefix::cmp::Eq for __BindgenUnionField<T> {
|
|
}
|
|
};
|
|
|
|
let items = vec![
|
|
union_field_decl,
|
|
union_field_impl,
|
|
union_field_default_impl,
|
|
union_field_clone_impl,
|
|
union_field_copy_impl,
|
|
union_field_debug_impl,
|
|
union_field_hash_impl,
|
|
union_field_partialeq_impl,
|
|
union_field_eq_impl,
|
|
];
|
|
|
|
let old_items = mem::replace(result, items);
|
|
result.extend(old_items);
|
|
}
|
|
|
|
pub(crate) fn prepend_incomplete_array_types(
|
|
ctx: &BindgenContext,
|
|
result: &mut Vec<proc_macro2::TokenStream>,
|
|
) {
|
|
let prefix = ctx.trait_prefix();
|
|
|
|
// If the target supports `const fn`, declare eligible functions
|
|
// as `const fn` else just `fn`.
|
|
let const_fn = if ctx.options().rust_features().min_const_fn {
|
|
quote! { const fn }
|
|
} else {
|
|
quote! { fn }
|
|
};
|
|
|
|
let incomplete_array_decl = quote! {
|
|
#[repr(C)]
|
|
#[derive(Default)]
|
|
pub struct __IncompleteArrayField<T>(
|
|
::#prefix::marker::PhantomData<T>, [T; 0]);
|
|
};
|
|
|
|
let from_raw_parts = ctx.wrap_unsafe_ops(quote! (
|
|
::#prefix::slice::from_raw_parts(self.as_ptr(), len)
|
|
));
|
|
let from_raw_parts_mut = ctx.wrap_unsafe_ops(quote! (
|
|
::#prefix::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
|
|
));
|
|
|
|
let incomplete_array_impl = quote! {
|
|
impl<T> __IncompleteArrayField<T> {
|
|
#[inline]
|
|
pub #const_fn new() -> Self {
|
|
__IncompleteArrayField(::#prefix::marker::PhantomData, [])
|
|
}
|
|
|
|
#[inline]
|
|
pub fn as_ptr(&self) -> *const T {
|
|
self as *const _ as *const T
|
|
}
|
|
|
|
#[inline]
|
|
pub fn as_mut_ptr(&mut self) -> *mut T {
|
|
self as *mut _ as *mut T
|
|
}
|
|
|
|
#[inline]
|
|
pub unsafe fn as_slice(&self, len: usize) -> &[T] {
|
|
#from_raw_parts
|
|
}
|
|
|
|
#[inline]
|
|
pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
|
|
#from_raw_parts_mut
|
|
}
|
|
}
|
|
};
|
|
|
|
let incomplete_array_debug_impl = quote! {
|
|
impl<T> ::#prefix::fmt::Debug for __IncompleteArrayField<T> {
|
|
fn fmt(&self, fmt: &mut ::#prefix::fmt::Formatter<'_>)
|
|
-> ::#prefix::fmt::Result {
|
|
fmt.write_str("__IncompleteArrayField")
|
|
}
|
|
}
|
|
};
|
|
|
|
let items = vec![
|
|
incomplete_array_decl,
|
|
incomplete_array_impl,
|
|
incomplete_array_debug_impl,
|
|
];
|
|
|
|
let old_items = mem::replace(result, items);
|
|
result.extend(old_items);
|
|
}
|
|
|
|
pub(crate) fn prepend_complex_type(
|
|
result: &mut Vec<proc_macro2::TokenStream>,
|
|
) {
|
|
let complex_type = quote! {
|
|
#[derive(PartialEq, Copy, Clone, Hash, Debug, Default)]
|
|
#[repr(C)]
|
|
pub struct __BindgenComplex<T> {
|
|
pub re: T,
|
|
pub im: T
|
|
}
|
|
};
|
|
|
|
let items = vec![complex_type];
|
|
let old_items = mem::replace(result, items);
|
|
result.extend(old_items);
|
|
}
|
|
|
|
pub(crate) fn build_path(
|
|
item: &Item,
|
|
ctx: &BindgenContext,
|
|
) -> error::Result<syn::Type> {
|
|
let path = item.namespace_aware_canonical_path(ctx);
|
|
let tokens =
|
|
proc_macro2::TokenStream::from_str(&path.join("::")).unwrap();
|
|
|
|
Ok(syn::parse_quote! { #tokens })
|
|
}
|
|
|
|
fn primitive_ty(ctx: &BindgenContext, name: &str) -> syn::Type {
|
|
let ident = ctx.rust_ident_raw(name);
|
|
syn::parse_quote! { #ident }
|
|
}
|
|
|
|
pub(crate) fn type_from_named(
|
|
ctx: &BindgenContext,
|
|
name: &str,
|
|
) -> Option<syn::Type> {
|
|
// FIXME: We could use the inner item to check this is really a
|
|
// primitive type but, who the heck overrides these anyway?
|
|
Some(match name {
|
|
"int8_t" => primitive_ty(ctx, "i8"),
|
|
"uint8_t" => primitive_ty(ctx, "u8"),
|
|
"int16_t" => primitive_ty(ctx, "i16"),
|
|
"uint16_t" => primitive_ty(ctx, "u16"),
|
|
"int32_t" => primitive_ty(ctx, "i32"),
|
|
"uint32_t" => primitive_ty(ctx, "u32"),
|
|
"int64_t" => primitive_ty(ctx, "i64"),
|
|
"uint64_t" => primitive_ty(ctx, "u64"),
|
|
|
|
"size_t" if ctx.options().size_t_is_usize => {
|
|
primitive_ty(ctx, "usize")
|
|
}
|
|
"uintptr_t" => primitive_ty(ctx, "usize"),
|
|
|
|
"ssize_t" if ctx.options().size_t_is_usize => {
|
|
primitive_ty(ctx, "isize")
|
|
}
|
|
"intptr_t" | "ptrdiff_t" => primitive_ty(ctx, "isize"),
|
|
_ => return None,
|
|
})
|
|
}
|
|
|
|
fn fnsig_return_ty_internal(
|
|
ctx: &BindgenContext,
|
|
sig: &FunctionSig,
|
|
) -> syn::Type {
|
|
if sig.is_divergent() {
|
|
return syn::parse_quote! { ! };
|
|
}
|
|
|
|
let canonical_type_kind = sig
|
|
.return_type()
|
|
.into_resolver()
|
|
.through_type_refs()
|
|
.through_type_aliases()
|
|
.resolve(ctx)
|
|
.kind()
|
|
.expect_type()
|
|
.kind();
|
|
|
|
match canonical_type_kind {
|
|
TypeKind::Void => syn::parse_quote! { () },
|
|
_ => sig.return_type().to_rust_ty_or_opaque(ctx, &()),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn fnsig_return_ty(
|
|
ctx: &BindgenContext,
|
|
sig: &FunctionSig,
|
|
) -> proc_macro2::TokenStream {
|
|
match fnsig_return_ty_internal(ctx, sig) {
|
|
syn::Type::Tuple(syn::TypeTuple { elems, .. })
|
|
if elems.is_empty() =>
|
|
{
|
|
quote! {}
|
|
}
|
|
ty => quote! { -> #ty },
|
|
}
|
|
}
|
|
|
|
pub(crate) fn fnsig_argument_type(
|
|
ctx: &BindgenContext,
|
|
ty: &TypeId,
|
|
) -> syn::Type {
|
|
use super::ToPtr;
|
|
|
|
let arg_item = ctx.resolve_item(ty);
|
|
let arg_ty = arg_item.kind().expect_type();
|
|
|
|
// From the C90 standard[1]:
|
|
//
|
|
// A declaration of a parameter as "array of type" shall be
|
|
// adjusted to "qualified pointer to type", where the type
|
|
// qualifiers (if any) are those specified within the [ and ] of
|
|
// the array type derivation.
|
|
//
|
|
// [1]: http://c0x.coding-guidelines.com/6.7.5.3.html
|
|
match *arg_ty.canonical_type(ctx).kind() {
|
|
TypeKind::Array(t, _) => {
|
|
let stream = if ctx.options().array_pointers_in_arguments {
|
|
arg_ty.to_rust_ty_or_opaque(ctx, arg_item)
|
|
} else {
|
|
t.to_rust_ty_or_opaque(ctx, &())
|
|
};
|
|
stream.to_ptr(ctx.resolve_type(t).is_const())
|
|
}
|
|
TypeKind::Pointer(inner) => {
|
|
let inner = ctx.resolve_item(inner);
|
|
let inner_ty = inner.expect_type();
|
|
if let TypeKind::ObjCInterface(ref interface) =
|
|
*inner_ty.canonical_type(ctx).kind()
|
|
{
|
|
let name = ctx.rust_ident(interface.name());
|
|
syn::parse_quote! { #name }
|
|
} else {
|
|
arg_item.to_rust_ty_or_opaque(ctx, &())
|
|
}
|
|
}
|
|
_ => arg_item.to_rust_ty_or_opaque(ctx, &()),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn fnsig_arguments_iter<
|
|
'a,
|
|
I: Iterator<Item = &'a (Option<String>, crate::ir::context::TypeId)>,
|
|
>(
|
|
ctx: &BindgenContext,
|
|
args_iter: I,
|
|
is_variadic: bool,
|
|
) -> Vec<proc_macro2::TokenStream> {
|
|
let mut unnamed_arguments = 0;
|
|
let mut args = args_iter
|
|
.map(|(name, ty)| {
|
|
let arg_ty = fnsig_argument_type(ctx, ty);
|
|
|
|
let arg_name = match *name {
|
|
Some(ref name) => ctx.rust_mangle(name).into_owned(),
|
|
None => {
|
|
unnamed_arguments += 1;
|
|
format!("arg{}", unnamed_arguments)
|
|
}
|
|
};
|
|
|
|
assert!(!arg_name.is_empty());
|
|
let arg_name = ctx.rust_ident(arg_name);
|
|
|
|
quote! {
|
|
#arg_name : #arg_ty
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
if is_variadic {
|
|
args.push(quote! { ... })
|
|
}
|
|
|
|
args
|
|
}
|
|
|
|
pub(crate) fn fnsig_arguments(
|
|
ctx: &BindgenContext,
|
|
sig: &FunctionSig,
|
|
) -> Vec<proc_macro2::TokenStream> {
|
|
fnsig_arguments_iter(
|
|
ctx,
|
|
sig.argument_types().iter(),
|
|
sig.is_variadic(),
|
|
)
|
|
}
|
|
|
|
pub(crate) fn fnsig_argument_identifiers(
|
|
ctx: &BindgenContext,
|
|
sig: &FunctionSig,
|
|
) -> Vec<proc_macro2::TokenStream> {
|
|
let mut unnamed_arguments = 0;
|
|
let args = sig
|
|
.argument_types()
|
|
.iter()
|
|
.map(|&(ref name, _ty)| {
|
|
let arg_name = match *name {
|
|
Some(ref name) => ctx.rust_mangle(name).into_owned(),
|
|
None => {
|
|
unnamed_arguments += 1;
|
|
format!("arg{}", unnamed_arguments)
|
|
}
|
|
};
|
|
|
|
assert!(!arg_name.is_empty());
|
|
let arg_name = ctx.rust_ident(arg_name);
|
|
|
|
quote! {
|
|
#arg_name
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
args
|
|
}
|
|
|
|
pub(crate) fn fnsig_block(
|
|
ctx: &BindgenContext,
|
|
sig: &FunctionSig,
|
|
) -> proc_macro2::TokenStream {
|
|
let args = sig.argument_types().iter().map(|&(_, ty)| {
|
|
let arg_item = ctx.resolve_item(ty);
|
|
|
|
arg_item.to_rust_ty_or_opaque(ctx, &())
|
|
});
|
|
|
|
let ret_ty = fnsig_return_ty_internal(ctx, sig);
|
|
quote! {
|
|
*const ::block::Block<(#(#args,)*), #ret_ty>
|
|
}
|
|
}
|
|
|
|
// Returns true if `canonical_name` will end up as `mangled_name` at the
|
|
// machine code level, i.e. after LLVM has applied any target specific
|
|
// mangling.
|
|
pub(crate) fn names_will_be_identical_after_mangling(
|
|
canonical_name: &str,
|
|
mangled_name: &str,
|
|
call_conv: Option<ClangAbi>,
|
|
) -> bool {
|
|
// If the mangled name and the canonical name are the same then no
|
|
// mangling can have happened between the two versions.
|
|
if canonical_name == mangled_name {
|
|
return true;
|
|
}
|
|
|
|
// Working with &[u8] makes indexing simpler than with &str
|
|
let canonical_name = canonical_name.as_bytes();
|
|
let mangled_name = mangled_name.as_bytes();
|
|
|
|
let (mangling_prefix, expect_suffix) = match call_conv {
|
|
Some(ClangAbi::Known(Abi::C)) |
|
|
// None is the case for global variables
|
|
None => {
|
|
(b'_', false)
|
|
}
|
|
Some(ClangAbi::Known(Abi::Stdcall)) => (b'_', true),
|
|
Some(ClangAbi::Known(Abi::Fastcall)) => (b'@', true),
|
|
|
|
// This is something we don't recognize, stay on the safe side
|
|
// by emitting the `#[link_name]` attribute
|
|
Some(_) => return false,
|
|
};
|
|
|
|
// Check that the mangled name is long enough to at least contain the
|
|
// canonical name plus the expected prefix.
|
|
if mangled_name.len() < canonical_name.len() + 1 {
|
|
return false;
|
|
}
|
|
|
|
// Return if the mangled name does not start with the prefix expected
|
|
// for the given calling convention.
|
|
if mangled_name[0] != mangling_prefix {
|
|
return false;
|
|
}
|
|
|
|
// Check that the mangled name contains the canonical name after the
|
|
// prefix
|
|
if &mangled_name[1..canonical_name.len() + 1] != canonical_name {
|
|
return false;
|
|
}
|
|
|
|
// If the given calling convention also prescribes a suffix, check that
|
|
// it exists too
|
|
if expect_suffix {
|
|
let suffix = &mangled_name[canonical_name.len() + 1..];
|
|
|
|
// The shortest suffix is "@0"
|
|
if suffix.len() < 2 {
|
|
return false;
|
|
}
|
|
|
|
// Check that the suffix starts with '@' and is all ASCII decimals
|
|
// after that.
|
|
if suffix[0] != b'@' || !suffix[1..].iter().all(u8::is_ascii_digit)
|
|
{
|
|
return false;
|
|
}
|
|
} else if mangled_name.len() != canonical_name.len() + 1 {
|
|
// If we don't expect a prefix but there is one, we need the
|
|
// #[link_name] attribute
|
|
return false;
|
|
}
|
|
|
|
true
|
|
}
|
|
}
|