diff --git a/bindgen/codegen/error.rs b/bindgen/codegen/error.rs index ead34965ba..2c9bf97680 100644 --- a/bindgen/codegen/error.rs +++ b/bindgen/codegen/error.rs @@ -11,17 +11,26 @@ pub(crate) enum Error { /// definition that is too difficult for us to understand (like a partial /// template specialization). InstantiationOfOpaqueType, + + /// Function ABI is not supported. + UnsupportedAbi(&'static str), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(match *self { Error::NoLayoutForOpaqueBlob => { - "Tried to generate an opaque blob, but had no layout" + "Tried to generate an opaque blob, but had no layout." } Error::InstantiationOfOpaqueType => { - "Instantiation of opaque template type or partial template \ - specialization" + "Instantiation of opaque template type or partial template specialization." + } + Error::UnsupportedAbi(abi) => { + return write!( + f, + "{} ABI is not supported by the configured Rust target.", + abi + ) } }) } diff --git a/bindgen/codegen/helpers.rs b/bindgen/codegen/helpers.rs index 8231e07c11..7ef44fe099 100644 --- a/bindgen/codegen/helpers.rs +++ b/bindgen/codegen/helpers.rs @@ -2,8 +2,6 @@ use crate::ir::context::BindgenContext; use crate::ir::layout::Layout; -use proc_macro2::{Ident, Span, TokenStream}; -use quote::TokenStreamExt; pub(crate) mod attributes { use proc_macro2::{Ident, Span, TokenStream}; @@ -79,33 +77,27 @@ pub(crate) mod attributes { /// Generates a proper type for a field or type with a given `Layout`, that is, /// a type with the correct size and alignment restrictions. -pub(crate) fn blob(ctx: &BindgenContext, layout: Layout) -> TokenStream { +pub(crate) fn blob(ctx: &BindgenContext, layout: Layout) -> syn::Type { let opaque = layout.opaque(); // FIXME(emilio, #412): We fall back to byte alignment, but there are // some things that legitimately are more than 8-byte aligned. // // Eventually we should be able to `unwrap` here, but... - let ty_name = match opaque.known_rust_type_for_array(ctx) { + let ty = match opaque.known_rust_type_for_array(ctx) { Some(ty) => ty, None => { warn!("Found unknown alignment on code generation!"); - "u8" + syn::parse_quote! { u8 } } }; - let ty_name = Ident::new(ty_name, Span::call_site()); - let data_len = opaque.array_size(ctx).unwrap_or(layout.size); if data_len == 1 { - quote! { - #ty_name - } + ty } else { - quote! { - [ #ty_name ; #data_len ] - } + syn::parse_quote! { [ #ty ; #data_len ] } } } @@ -113,80 +105,118 @@ pub(crate) fn blob(ctx: &BindgenContext, layout: Layout) -> TokenStream { pub(crate) fn integer_type( ctx: &BindgenContext, layout: Layout, -) -> Option { - let name = Layout::known_type_for_size(ctx, layout.size)?; - let name = Ident::new(name, Span::call_site()); - Some(quote! { #name }) +) -> Option { + Layout::known_type_for_size(ctx, layout.size) } /// Generates a bitfield allocation unit type for a type with the given `Layout`. -pub(crate) fn bitfield_unit( - ctx: &BindgenContext, - layout: Layout, -) -> TokenStream { - let mut tokens = quote! {}; +pub(crate) fn bitfield_unit(ctx: &BindgenContext, layout: Layout) -> syn::Type { + let size = layout.size; + let ty = syn::parse_quote! { __BindgenBitfieldUnit<[u8; #size]> }; if ctx.options().enable_cxx_namespaces { - tokens.append_all(quote! { root:: }); + return syn::parse_quote! { root::#ty }; } - let size = layout.size; - tokens.append_all(quote! { - __BindgenBitfieldUnit<[u8; #size]> - }); - - tokens + ty } pub(crate) mod ast_ty { use crate::ir::context::BindgenContext; use crate::ir::function::FunctionSig; use crate::ir::layout::Layout; - use crate::ir::ty::FloatKind; + use crate::ir::ty::{FloatKind, IntKind}; use proc_macro2::{self, TokenStream}; use std::str::FromStr; - pub(crate) fn c_void(ctx: &BindgenContext) -> TokenStream { + pub(crate) fn c_void(ctx: &BindgenContext) -> syn::Type { // ctypes_prefix takes precedence match ctx.options().ctypes_prefix { Some(ref prefix) => { let prefix = TokenStream::from_str(prefix.as_str()).unwrap(); - quote! { - #prefix::c_void - } + syn::parse_quote! { #prefix::c_void } } None => { if ctx.options().use_core && ctx.options().rust_features.core_ffi_c_void { - quote! { ::core::ffi::c_void } + syn::parse_quote! { ::core::ffi::c_void } } else { - quote! { ::std::os::raw::c_void } + syn::parse_quote! { ::std::os::raw::c_void } } } } } - pub(crate) fn raw_type(ctx: &BindgenContext, name: &str) -> TokenStream { + pub(crate) fn raw_type(ctx: &BindgenContext, name: &str) -> syn::Type { let ident = ctx.rust_ident_raw(name); match ctx.options().ctypes_prefix { Some(ref prefix) => { let prefix = TokenStream::from_str(prefix.as_str()).unwrap(); - quote! { - #prefix::#ident - } + syn::parse_quote! { #prefix::#ident } } None => { if ctx.options().use_core && ctx.options().rust_features().core_ffi_c { - quote! { - ::core::ffi::#ident - } + syn::parse_quote! { ::core::ffi::#ident } } else { - quote! { - ::std::os::raw::#ident - } + syn::parse_quote! { ::std::os::raw::#ident } + } + } + } + } + + pub(crate) fn int_kind_rust_type( + ctx: &BindgenContext, + ik: IntKind, + layout: Option, + ) -> syn::Type { + match ik { + IntKind::Bool => syn::parse_quote! { bool }, + IntKind::Char { .. } => raw_type(ctx, "c_char"), + IntKind::SChar => raw_type(ctx, "c_schar"), + IntKind::UChar => raw_type(ctx, "c_uchar"), + IntKind::Short => raw_type(ctx, "c_short"), + IntKind::UShort => raw_type(ctx, "c_ushort"), + IntKind::Int => raw_type(ctx, "c_int"), + IntKind::UInt => raw_type(ctx, "c_uint"), + IntKind::Long => raw_type(ctx, "c_long"), + IntKind::ULong => raw_type(ctx, "c_ulong"), + IntKind::LongLong => raw_type(ctx, "c_longlong"), + IntKind::ULongLong => raw_type(ctx, "c_ulonglong"), + IntKind::WChar => { + let layout = + layout.expect("Couldn't compute wchar_t's layout?"); + Layout::known_type_for_size(ctx, layout.size) + .expect("Non-representable wchar_t?") + } + + IntKind::I8 => syn::parse_quote! { i8 }, + IntKind::U8 => syn::parse_quote! { u8 }, + IntKind::I16 => syn::parse_quote! { i16 }, + IntKind::U16 => syn::parse_quote! { u16 }, + IntKind::I32 => syn::parse_quote! { i32 }, + IntKind::U32 => syn::parse_quote! { u32 }, + IntKind::I64 => syn::parse_quote! { i64 }, + IntKind::U64 => syn::parse_quote! { u64 }, + IntKind::Custom { name, .. } => { + syn::parse_str(name).expect("Invalid integer type.") + } + IntKind::U128 => { + if ctx.options().rust_features.i128_and_u128 { + syn::parse_quote! { u128 } + } else { + // Best effort thing, but wrong alignment + // unfortunately. + syn::parse_quote! { [u64; 2] } + } + } + IntKind::I128 => { + if ctx.options().rust_features.i128_and_u128 { + syn::parse_quote! { i128 } + } else { + syn::parse_quote! { [u64; 2] } } } } @@ -196,26 +226,26 @@ pub(crate) mod ast_ty { ctx: &BindgenContext, fk: FloatKind, layout: Option, - ) -> TokenStream { + ) -> syn::Type { // TODO: we probably should take the type layout into account more // often? // // Also, maybe this one shouldn't be the default? match (fk, ctx.options().convert_floats) { - (FloatKind::Float, true) => quote! { f32 }, - (FloatKind::Double, true) => quote! { f64 }, + (FloatKind::Float, true) => syn::parse_quote! { f32 }, + (FloatKind::Double, true) => syn::parse_quote! { f64 }, (FloatKind::Float, false) => raw_type(ctx, "c_float"), (FloatKind::Double, false) => raw_type(ctx, "c_double"), (FloatKind::LongDouble, _) => { match layout { Some(layout) => { match layout.size { - 4 => quote! { f32 }, - 8 => quote! { f64 }, + 4 => syn::parse_quote! { f32 }, + 8 => syn::parse_quote! { f64 }, // TODO(emilio): If rust ever gains f128 we should // use it here and below. _ => super::integer_type(ctx, layout) - .unwrap_or(quote! { f64 }), + .unwrap_or(syn::parse_quote! { f64 }), } } None => { @@ -223,15 +253,15 @@ pub(crate) mod ast_ty { false, "How didn't we know the layout for a primitive type?" ); - quote! { f64 } + syn::parse_quote! { f64 } } } } (FloatKind::Float128, _) => { if ctx.options().rust_features.i128_and_u128 { - quote! { u128 } + syn::parse_quote! { u128 } } else { - quote! { [u64; 2] } + syn::parse_quote! { [u64; 2] } } } } diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index c381f57cb8..11425e02a4 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -1,5 +1,6 @@ mod dyngen; -mod error; +pub(crate) mod error; + mod helpers; mod impl_debug; mod impl_partialeq; @@ -36,7 +37,7 @@ use crate::ir::derive::{ use crate::ir::dot; use crate::ir::enum_ty::{Enum, EnumVariant, EnumVariantValue}; use crate::ir::function::{ - Abi, ClangAbi, Function, FunctionKind, FunctionSig, Linkage, + ClangAbi, Function, FunctionKind, FunctionSig, Linkage, }; use crate::ir::int::IntKind; use crate::ir::item::{IsOpaque, Item, ItemCanonicalName, ItemCanonicalPath}; @@ -391,45 +392,44 @@ impl<'a> ops::DerefMut for CodegenResult<'a> { /// 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) -> proc_macro2::TokenStream; + fn to_ptr(self, is_const: bool) -> syn::Type; } -impl ToPtr for proc_macro2::TokenStream { - fn to_ptr(self, is_const: bool) -> proc_macro2::TokenStream { +impl ToPtr for syn::Type { + fn to_ptr(self, is_const: bool) -> syn::Type { if is_const { - quote! { *const #self } + syn::parse_quote! { *const #self } } else { - quote! { *mut #self } + syn::parse_quote! { *mut #self } } } } -/// An extension trait for `proc_macro2::TokenStream` that lets us append any implicit +/// An extension trait for `syn::Type` that lets us append any implicit /// template parameters that exist for some type, if necessary. -trait AppendImplicitTemplateParams { - fn append_implicit_template_params( - &mut self, +trait WithImplicitTemplateParams { + fn with_implicit_template_params( + self, ctx: &BindgenContext, item: &Item, - ); + ) -> Self; } -impl AppendImplicitTemplateParams for proc_macro2::TokenStream { - fn append_implicit_template_params( - &mut 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); - match *item.expect_type().kind() { + 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 | @@ -445,22 +445,25 @@ impl AppendImplicitTemplateParams for proc_macro2::TokenStream { TypeKind::Enum(..) | TypeKind::ObjCId | TypeKind::ObjCSel | - TypeKind::TemplateInstantiation(..) => return, - _ => {} - } + 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", + ) + })) + } + } + }; - let params: Vec<_> = item - .used_template_params(ctx) - .iter() - .map(|p| { - p.try_to_rust_ty(ctx, &()) - .expect("template params cannot fail to be a rust type") - }) - .collect(); - if !params.is_empty() { - self.append_all(quote! { - < #( #params ),* > - }); + if let Some(params) = params { + syn::parse_quote! { #self<#(#params),*> } + } else { + self } } } @@ -670,7 +673,8 @@ impl CodeGenerator for Var { attrs.push(attributes::doc(comment)); } - let ty = self.ty().to_rust_ty_or_opaque(ctx, &()); + let var_ty = self.ty(); + let ty = var_ty.to_rust_ty_or_opaque(ctx, &()); if let Some(val) = self.val() { match *val { @@ -681,8 +685,7 @@ impl CodeGenerator for Var { }); } VarType::Int(val) => { - let int_kind = self - .ty() + let int_kind = var_ty .into_resolver() .through_type_aliases() .through_type_refs() @@ -929,11 +932,10 @@ impl CodeGenerator for Type { // 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. - let mut inner_ty = inner_item + inner_item .try_to_rust_ty_or_opaque(ctx, &()) - .unwrap_or_else(|_| self.to_opaque(ctx, item)); - inner_ty.append_implicit_template_params(ctx, inner_item); - inner_ty + .unwrap_or_else(|_| self.to_opaque(ctx, item)) + .with_implicit_template_params(ctx, inner_item) }; { @@ -979,9 +981,8 @@ impl CodeGenerator for Type { // We prefer using `pub use` over `pub type` because of: // https://github.com/rust-lang/rust/issues/26264 - // These are the only characters allowed in simple - // paths, eg `good::dogs::Bront`. - if inner_rust_type.to_string().chars().all(|c| matches!(c, 'A'..='Z' | 'a'..='z' | '0'..='9' | ':' | '_' | ' ')) && outer_params.is_empty() && + 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() @@ -1208,11 +1209,9 @@ impl<'a> TryToRustTy for Vtable<'a> { &self, ctx: &BindgenContext, _: &(), - ) -> error::Result { + ) -> error::Result { let name = ctx.rust_ident(self.canonical_name(ctx)); - Ok(quote! { - #name - }) + Ok(syn::parse_quote! { #name }) } } @@ -1364,28 +1363,22 @@ impl<'a> FieldCodegen<'a> for Field { fn wrap_union_field_if_needed( ctx: &BindgenContext, struct_layout: &StructLayoutTracker, - ty: proc_macro2::TokenStream, + ty: syn::Type, result: &mut CodegenResult, -) -> proc_macro2::TokenStream { +) -> syn::Type { if struct_layout.is_rust_union() { if struct_layout.can_copy_union_fields() { ty } else { let prefix = ctx.trait_prefix(); - quote! { - ::#prefix::mem::ManuallyDrop<#ty> - } + syn::parse_quote! { ::#prefix::mem::ManuallyDrop<#ty> } } } else { result.saw_bindgen_union(); if ctx.options().enable_cxx_namespaces { - quote! { - root::__BindgenUnionField<#ty> - } + syn::parse_quote! { root::__BindgenUnionField<#ty> } } else { - quote! { - __BindgenUnionField<#ty> - } + syn::parse_quote! { __BindgenUnionField<#ty> } } } } @@ -1416,8 +1409,10 @@ impl<'a> FieldCodegen<'a> for FieldData { let field_item = self.ty().into_resolver().through_type_refs().resolve(ctx); let field_ty = field_item.expect_type(); - let mut ty = self.ty().to_rust_ty_or_opaque(ctx, &()); - ty.append_implicit_template_params(ctx, field_item); + 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() { @@ -1428,13 +1423,9 @@ impl<'a> FieldCodegen<'a> for FieldData { let inner = item.to_rust_ty_or_opaque(ctx, &()); if ctx.options().enable_cxx_namespaces { - quote! { - root::__IncompleteArrayField<#inner> - } + syn::parse_quote! { root::__IncompleteArrayField<#inner> } } else { - quote! { - __IncompleteArrayField<#inner> - } + syn::parse_quote! { __IncompleteArrayField<#inner> } } } else { ty @@ -2004,8 +1995,9 @@ impl CodeGenerator for CompInfo { } let inner_item = ctx.resolve_item(base.ty); - let mut inner = inner_item.to_rust_ty_or_opaque(ctx, &()); - inner.append_implicit_template_params(ctx, inner_item); + 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()); @@ -2607,22 +2599,7 @@ impl Method { _ => panic!("How in the world?"), }; - let supported_abi = match signature.abi(ctx, Some(&*name)) { - ClangAbi::Known(Abi::ThisCall) => { - ctx.options().rust_features().thiscall_abi - } - ClangAbi::Known(Abi::Vectorcall) => { - ctx.options().rust_features().vectorcall_abi - } - ClangAbi::Known(Abi::CUnwind) => { - ctx.options().rust_features().c_unwind_abi - } - ClangAbi::Known(Abi::EfiApi) => { - ctx.options().rust_features().abi_efiapi - } - _ => true, - }; - + let supported_abi = signature.abi(ctx, Some(&*name)).is_ok(); if !supported_abi { return; } @@ -2891,7 +2868,7 @@ impl<'a> EnumBuilder<'a> { fn new( name: &'a str, mut attrs: Vec, - repr: proc_macro2::TokenStream, + repr: syn::Type, enum_variation: EnumVariation, has_typedef: bool, ) -> Self { @@ -2960,7 +2937,7 @@ impl<'a> EnumBuilder<'a> { ctx: &BindgenContext, variant: &EnumVariant, mangling_prefix: Option<&str>, - rust_ty: proc_macro2::TokenStream, + rust_ty: syn::Type, result: &mut CodegenResult<'_>, is_ty_named: bool, ) -> Self { @@ -3075,7 +3052,7 @@ impl<'a> EnumBuilder<'a> { fn build( self, ctx: &BindgenContext, - rust_ty: proc_macro2::TokenStream, + rust_ty: syn::Type, result: &mut CodegenResult<'_>, ) -> proc_macro2::TokenStream { match self { @@ -3326,7 +3303,7 @@ impl CodeGenerator for Enum { // value. variant_name: &Ident, referenced_name: &Ident, - enum_rust_ty: proc_macro2::TokenStream, + enum_rust_ty: syn::Type, result: &mut CodegenResult<'_>, ) { let constant_name = if enum_.name().is_some() { @@ -3641,7 +3618,7 @@ impl std::str::FromStr for NonCopyUnionStyle { /// 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. -trait TryToOpaque { +pub(crate) trait TryToOpaque { type Extra; /// Get the layout for this thing, if one is available. @@ -3656,7 +3633,7 @@ trait TryToOpaque { &self, ctx: &BindgenContext, extra: &Self::Extra, - ) -> error::Result { + ) -> error::Result { self.try_get_layout(ctx, extra) .map(|layout| helpers::blob(ctx, layout)) } @@ -3672,7 +3649,7 @@ trait TryToOpaque { /// /// Don't implement this directly. Instead implement `TryToOpaque`, and then /// leverage the blanket impl for this trait. -trait ToOpaque: TryToOpaque { +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)) @@ -3682,7 +3659,7 @@ trait ToOpaque: TryToOpaque { &self, ctx: &BindgenContext, extra: &Self::Extra, - ) -> proc_macro2::TokenStream { + ) -> syn::Type { let layout = self.get_layout(ctx, extra); helpers::blob(ctx, layout) } @@ -3697,14 +3674,14 @@ impl ToOpaque for T where T: TryToOpaque {} /// 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. -trait TryToRustTy { +pub(crate) trait TryToRustTy { type Extra; fn try_to_rust_ty( &self, ctx: &BindgenContext, extra: &Self::Extra, - ) -> error::Result; + ) -> error::Result; } /// Fallible conversion to a Rust type or an opaque blob with the correct size @@ -3712,14 +3689,14 @@ trait TryToRustTy { /// /// Don't implement this directly. Instead implement `TryToRustTy` and /// `TryToOpaque`, and then leverage the blanket impl for this trait below. -trait TryToRustTyOrOpaque: TryToRustTy + TryToOpaque { +pub(crate) trait TryToRustTyOrOpaque: TryToRustTy + TryToOpaque { type Extra; fn try_to_rust_ty_or_opaque( &self, ctx: &BindgenContext, extra: &::Extra, - ) -> error::Result; + ) -> error::Result; } impl TryToRustTyOrOpaque for T @@ -3732,7 +3709,7 @@ where &self, ctx: &BindgenContext, extra: &E, - ) -> error::Result { + ) -> error::Result { self.try_to_rust_ty(ctx, extra).or_else(|_| { if let Ok(layout) = self.try_get_layout(ctx, extra) { Ok(helpers::blob(ctx, layout)) @@ -3760,14 +3737,14 @@ where /// `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. -trait ToRustTyOrOpaque: TryToRustTy + ToOpaque { +pub(crate) trait ToRustTyOrOpaque: TryToRustTy + ToOpaque { type Extra; fn to_rust_ty_or_opaque( &self, ctx: &BindgenContext, extra: &::Extra, - ) -> proc_macro2::TokenStream; + ) -> syn::Type; } impl ToRustTyOrOpaque for T @@ -3780,7 +3757,7 @@ where &self, ctx: &BindgenContext, extra: &E, - ) -> proc_macro2::TokenStream { + ) -> syn::Type { self.try_to_rust_ty(ctx, extra) .unwrap_or_else(|_| self.to_opaque(ctx, extra)) } @@ -3811,7 +3788,7 @@ where &self, ctx: &BindgenContext, _: &(), - ) -> error::Result { + ) -> error::Result { ctx.resolve_item((*self).into()).try_to_rust_ty(ctx, &()) } } @@ -3835,7 +3812,7 @@ impl TryToRustTy for Item { &self, ctx: &BindgenContext, _: &(), - ) -> error::Result { + ) -> error::Result { self.kind().expect_type().try_to_rust_ty(ctx, self) } } @@ -3859,7 +3836,7 @@ impl TryToRustTy for Type { &self, ctx: &BindgenContext, item: &Item, - ) -> error::Result { + ) -> error::Result { use self::helpers::ast_ty::*; match *self.kind() { @@ -3868,57 +3845,7 @@ impl TryToRustTy for Type { // c_void is enough? TypeKind::NullPtr => Ok(c_void(ctx).to_ptr(true)), TypeKind::Int(ik) => { - match ik { - IntKind::Bool => Ok(quote! { bool }), - IntKind::Char { .. } => Ok(raw_type(ctx, "c_char")), - IntKind::SChar => Ok(raw_type(ctx, "c_schar")), - IntKind::UChar => Ok(raw_type(ctx, "c_uchar")), - IntKind::Short => Ok(raw_type(ctx, "c_short")), - IntKind::UShort => Ok(raw_type(ctx, "c_ushort")), - IntKind::Int => Ok(raw_type(ctx, "c_int")), - IntKind::UInt => Ok(raw_type(ctx, "c_uint")), - IntKind::Long => Ok(raw_type(ctx, "c_long")), - IntKind::ULong => Ok(raw_type(ctx, "c_ulong")), - IntKind::LongLong => Ok(raw_type(ctx, "c_longlong")), - IntKind::ULongLong => Ok(raw_type(ctx, "c_ulonglong")), - IntKind::WChar => { - let layout = self - .layout(ctx) - .expect("Couldn't compute wchar_t's layout?"); - let ty = Layout::known_type_for_size(ctx, layout.size) - .expect("Non-representable wchar_t?"); - let ident = ctx.rust_ident_raw(ty); - Ok(quote! { #ident }) - } - - IntKind::I8 => Ok(quote! { i8 }), - IntKind::U8 => Ok(quote! { u8 }), - IntKind::I16 => Ok(quote! { i16 }), - IntKind::U16 => Ok(quote! { u16 }), - IntKind::I32 => Ok(quote! { i32 }), - IntKind::U32 => Ok(quote! { u32 }), - IntKind::I64 => Ok(quote! { i64 }), - IntKind::U64 => Ok(quote! { u64 }), - IntKind::Custom { name, .. } => { - Ok(proc_macro2::TokenStream::from_str(name).unwrap()) - } - IntKind::U128 => { - Ok(if ctx.options().rust_features.i128_and_u128 { - quote! { u128 } - } else { - // Best effort thing, but wrong alignment - // unfortunately. - quote! { [u64; 2] } - }) - } - IntKind::I128 => { - Ok(if ctx.options().rust_features.i128_and_u128 { - quote! { i128 } - } else { - quote! { [u64; 2] } - }) - } - } + Ok(int_kind_rust_type(ctx, ik, self.layout(ctx))) } TypeKind::Float(fk) => { Ok(float_kind_rust_type(ctx, fk, self.layout(ctx))) @@ -3929,38 +3856,30 @@ impl TryToRustTy for Type { ctx.generated_bindgen_complex(); Ok(if ctx.options().enable_cxx_namespaces { - quote! { - root::__BindgenComplex<#float_path> - } + syn::parse_quote! { root::__BindgenComplex<#float_path> } } else { - quote! { - __BindgenComplex<#float_path> - } + syn::parse_quote! { __BindgenComplex<#float_path> } }) } - TypeKind::Function(ref fs) => { + TypeKind::Function(ref signature) => { // We can't rely on the sizeof(Option>) == // sizeof(NonZero<_>) optimization with opaque blobs (because // they aren't NonZero), so don't *ever* use an or_opaque // variant here. - let ty = fs.try_to_rust_ty(ctx, &())?; + let ty = signature.try_to_rust_ty(ctx, item)?; let prefix = ctx.trait_prefix(); - Ok(quote! { - ::#prefix::option::Option<#ty> - }) + 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(quote! { - [ #ty ; #len ] - }) + 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(quote!(#path)) + Ok(syn::parse_quote!(#path)) } TypeKind::TemplateInstantiation(ref inst) => { inst.try_to_rust_ty(ctx, item) @@ -4013,8 +3932,9 @@ impl TryToRustTy for Type { // 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 mut ty = inner.to_rust_ty_or_opaque(ctx, &()); - ty.append_implicit_template_params(ctx, inner); + 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. @@ -4028,21 +3948,13 @@ impl TryToRustTy for Type { TypeKind::TypeParam => { let name = item.canonical_name(ctx); let ident = ctx.rust_ident(name); - Ok(quote! { - #ident - }) + Ok(syn::parse_quote! { #ident }) } - TypeKind::ObjCSel => Ok(quote! { - objc::runtime::Sel - }), - TypeKind::ObjCId => Ok(quote! { - id - }), + 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(quote! { - #name - }) + Ok(syn::parse_quote! { #name }) } ref u @ TypeKind::UnresolvedTypeRef(..) => { unreachable!("Should have been resolved after parsing {:?}!", u) @@ -4072,7 +3984,7 @@ impl TryToRustTy for TemplateInstantiation { &self, ctx: &BindgenContext, item: &Item, - ) -> error::Result { + ) -> error::Result { if self.is_opaque(ctx, item) { return Err(error::Error::InstantiationOfOpaqueType); } @@ -4116,62 +4028,50 @@ impl TryToRustTy for TemplateInstantiation { .filter(|&(_, param)| ctx.uses_template_parameter(def.id(), *param)) .map(|(arg, _)| { let arg = arg.into_resolver().through_type_refs().resolve(ctx); - let mut ty = arg.try_to_rust_ty(ctx, &())?; - ty.append_implicit_template_params(ctx, arg); + let ty = arg + .try_to_rust_ty(ctx, &())? + .with_implicit_template_params(ctx, arg); Ok(ty) }) .collect::>>()?; - if template_args.is_empty() { - return Ok(ty); - } - - Ok(quote! { - #ty < #( #template_args ),* > + Ok(if template_args.is_empty() { + syn::parse_quote! { #ty } + } else { + syn::parse_quote! { #ty<#(#template_args),*> } }) } } impl TryToRustTy for FunctionSig { - type Extra = (); + type Extra = Item; fn try_to_rust_ty( &self, ctx: &BindgenContext, - _: &(), - ) -> error::Result { + item: &Item, + ) -> error::Result { // 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) { - ClangAbi::Known(Abi::ThisCall) - if !ctx.options().rust_features().thiscall_abi => - { - warn!("Skipping function with thiscall ABI that isn't supported by the configured Rust target"); - Ok(proc_macro2::TokenStream::new()) - } - ClangAbi::Known(Abi::Vectorcall) - if !ctx.options().rust_features().vectorcall_abi => - { - warn!("Skipping function with vectorcall ABI that isn't supported by the configured Rust target"); - Ok(proc_macro2::TokenStream::new()) - } - ClangAbi::Known(Abi::CUnwind) - if !ctx.options().rust_features().c_unwind_abi => - { - warn!("Skipping function with C-unwind ABI that isn't supported by the configured Rust target"); - Ok(proc_macro2::TokenStream::new()) - } - ClangAbi::Known(Abi::EfiApi) - if !ctx.options().rust_features().abi_efiapi => - { - warn!("Skipping function with efiapi ABI that isn't supported by the configured Rust target"); - Ok(proc_macro2::TokenStream::new()) + 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) } - abi => Ok(quote! { - unsafe extern #abi fn ( #( #arguments ),* ) #ret - }), } } } @@ -4275,66 +4175,26 @@ impl CodeGenerator for Function { } let abi = match signature.abi(ctx, Some(name)) { - ClangAbi::Known(Abi::ThisCall) - if !ctx.options().rust_features().thiscall_abi => - { - unsupported_abi_diagnostic::( - name, - item.location(), - "thiscall", - ctx, - ); - return None; - } - ClangAbi::Known(Abi::Vectorcall) - if !ctx.options().rust_features().vectorcall_abi => - { - unsupported_abi_diagnostic::( - name, - item.location(), - "vectorcall", - ctx, - ); - return None; - } - ClangAbi::Known(Abi::CUnwind) - if !ctx.options().rust_features().c_unwind_abi => - { - unsupported_abi_diagnostic::( - name, - item.location(), - "C-unwind", - ctx, - ); - return None; - } - ClangAbi::Known(Abi::EfiApi) - if !ctx.options().rust_features().abi_efiapi => - { - unsupported_abi_diagnostic::( - name, - item.location(), - "efiapi", - ctx, - ); - return None; - } - ClangAbi::Known(Abi::Win64) if signature.is_variadic() => { - unsupported_abi_diagnostic::( - name, - item.location(), - "Win64", - ctx, - ); + Err(err) => { + if matches!(err, error::Error::UnsupportedAbi(_)) { + unsupported_abi_diagnostic( + name, + signature.is_variadic(), + item.location(), + ctx, + &err, + ); + } + return None; } - ClangAbi::Unknown(unknown_abi) => { + Ok(ClangAbi::Unknown(unknown_abi)) => { panic!( "Invalid or unknown abi {:?} for function {:?} ({:?})", unknown_abi, canonical_name, self ); } - abi => abi, + Ok(abi) => abi, }; // Handle overloaded functions by giving each overload its own unique @@ -4452,34 +4312,48 @@ impl CodeGenerator for Function { } } -fn unsupported_abi_diagnostic( +#[cfg_attr(not(feature = "experimental"), allow(unused_variables))] +fn unsupported_abi_diagnostic( fn_name: &str, - _location: Option<&crate::clang::SourceLocation>, - abi: &str, - _ctx: &BindgenContext, + variadic: bool, + location: Option<&crate::clang::SourceLocation>, + ctx: &BindgenContext, + error: &error::Error, ) { warn!( - "Skipping {}function `{}` with the {} ABI that isn't supported by the configured Rust target", - if VARIADIC { "variadic " } else { "" }, + "Skipping {}function `{}` because the {}", + if variadic { "variadic " } else { "" }, fn_name, - abi + error ); #[cfg(feature = "experimental")] - if _ctx.options().emit_diagnostics { + if ctx.options().emit_diagnostics { use crate::diagnostics::{get_line, Diagnostic, Level, Slice}; let mut diag = Diagnostic::default(); - diag - .with_title(format!( - "The `{}` {}function uses the {} ABI which is not supported by the configured Rust target.", + diag.with_title( + format!( + "Skipping {}function `{}` because the {}", + if variadic { "variadic " } else { "" }, fn_name, - if VARIADIC { "variadic " } else { "" }, - abi), Level::Warn) - .add_annotation("No code will be generated for this function.", Level::Warn) - .add_annotation(format!("The configured Rust version is {}.", String::from(_ctx.options().rust_target)), Level::Note); + error + ), + Level::Warn, + ) + .add_annotation( + "No code will be generated for this function.", + Level::Warn, + ) + .add_annotation( + format!( + "The configured Rust version is {}.", + String::from(ctx.options().rust_target) + ), + Level::Note, + ); - if let Some(loc) = _location { + if let Some(loc) = location { let (file, line, col, _) = loc.location(); if let Some(filename) = file.name() { @@ -4868,6 +4742,7 @@ 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; @@ -5267,28 +5142,23 @@ pub(crate) mod utils { pub(crate) fn build_path( item: &Item, ctx: &BindgenContext, - ) -> error::Result { + ) -> error::Result { let path = item.namespace_aware_canonical_path(ctx); let tokens = proc_macro2::TokenStream::from_str(&path.join("::")).unwrap(); - Ok(tokens) + Ok(syn::parse_quote! { #tokens }) } - fn primitive_ty( - ctx: &BindgenContext, - name: &str, - ) -> proc_macro2::TokenStream { + fn primitive_ty(ctx: &BindgenContext, name: &str) -> syn::Type { let ident = ctx.rust_ident_raw(name); - quote! { - #ident - } + syn::parse_quote! { #ident } } pub(crate) fn type_from_named( ctx: &BindgenContext, name: &str, - ) -> Option { + ) -> Option { // 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 { @@ -5317,14 +5187,9 @@ pub(crate) mod utils { fn fnsig_return_ty_internal( ctx: &BindgenContext, sig: &FunctionSig, - include_arrow: bool, - ) -> proc_macro2::TokenStream { + ) -> syn::Type { if sig.is_divergent() { - return if include_arrow { - quote! { -> ! } - } else { - quote! { ! } - }; + return syn::parse_quote! { ! }; } let canonical_type_kind = sig @@ -5337,19 +5202,9 @@ pub(crate) mod utils { .expect_type() .kind(); - if let TypeKind::Void = canonical_type_kind { - return if include_arrow { - quote! {} - } else { - quote! { () } - }; - } - - let ret_ty = sig.return_type().to_rust_ty_or_opaque(ctx, &()); - if include_arrow { - quote! { -> #ret_ty } - } else { - ret_ty + match canonical_type_kind { + TypeKind::Void => syn::parse_quote! { () }, + _ => sig.return_type().to_rust_ty_or_opaque(ctx, &()), } } @@ -5357,7 +5212,56 @@ pub(crate) mod utils { ctx: &BindgenContext, sig: &FunctionSig, ) -> proc_macro2::TokenStream { - fnsig_return_ty_internal(ctx, sig, /* include_arrow = */ true) + 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< @@ -5368,48 +5272,10 @@ pub(crate) mod utils { args_iter: I, is_variadic: bool, ) -> Vec { - use super::ToPtr; - let mut unnamed_arguments = 0; let mut args = args_iter - .map(|&(ref name, ty)| { - 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 - let arg_ty = 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()); - quote! { - #name - } - } else { - arg_item.to_rust_ty_or_opaque(ctx, &()) - } - } - _ => arg_item.to_rust_ty_or_opaque(ctx, &()), - }; + .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(), @@ -5485,9 +5351,7 @@ pub(crate) mod utils { arg_item.to_rust_ty_or_opaque(ctx, &()) }); - let ret_ty = fnsig_return_ty_internal( - ctx, sig, /* include_arrow = */ false, - ); + let ret_ty = fnsig_return_ty_internal(ctx, sig); quote! { *const ::block::Block<(#(#args,)*), #ret_ty> } diff --git a/bindgen/ir/function.rs b/bindgen/ir/function.rs index fab380e45f..a0781a3cda 100644 --- a/bindgen/ir/function.rs +++ b/bindgen/ir/function.rs @@ -405,6 +405,11 @@ fn args_from_ty_and_cursor( } impl FunctionSig { + /// Get the function name. + pub(crate) fn name(&self) -> &str { + &self.name + } + /// Construct a new function signature from the given Clang type. pub(crate) fn from_ty( ty: &clang::Type, @@ -605,10 +610,10 @@ impl FunctionSig { &self, ctx: &BindgenContext, name: Option<&str>, - ) -> ClangAbi { + ) -> crate::codegen::error::Result { // FIXME (pvdrz): Try to do this check lazily instead. Maybe store the ABI inside `ctx` // instead?. - if let Some(name) = name { + let abi = if let Some(name) = name { if let Some((abi, _)) = ctx .options() .abi_overrides @@ -628,6 +633,33 @@ impl FunctionSig { ClangAbi::Known(*abi) } else { self.abi + }; + + match abi { + ClangAbi::Known(Abi::ThisCall) + if !ctx.options().rust_features().thiscall_abi => + { + Err(crate::codegen::error::Error::UnsupportedAbi("thiscall")) + } + ClangAbi::Known(Abi::Vectorcall) + if !ctx.options().rust_features().vectorcall_abi => + { + Err(crate::codegen::error::Error::UnsupportedAbi("vectorcall")) + } + ClangAbi::Known(Abi::CUnwind) + if !ctx.options().rust_features().c_unwind_abi => + { + Err(crate::codegen::error::Error::UnsupportedAbi("C-unwind")) + } + ClangAbi::Known(Abi::EfiApi) + if !ctx.options().rust_features().abi_efiapi => + { + Err(crate::codegen::error::Error::UnsupportedAbi("efiapi")) + } + ClangAbi::Known(Abi::Win64) if self.is_variadic() => { + Err(crate::codegen::error::Error::UnsupportedAbi("Win64")) + } + abi => Ok(abi), } } diff --git a/bindgen/ir/layout.rs b/bindgen/ir/layout.rs index ba944b06bc..85a553da31 100644 --- a/bindgen/ir/layout.rs +++ b/bindgen/ir/layout.rs @@ -37,13 +37,15 @@ impl Layout { pub(crate) fn known_type_for_size( ctx: &BindgenContext, size: usize, - ) -> Option<&'static str> { + ) -> Option { Some(match size { - 16 if ctx.options().rust_features.i128_and_u128 => "u128", - 8 => "u64", - 4 => "u32", - 2 => "u16", - 1 => "u8", + 16 if ctx.options().rust_features.i128_and_u128 => { + syn::parse_quote! { u128 } + } + 8 => syn::parse_quote! { u64 }, + 4 => syn::parse_quote! { u32 }, + 2 => syn::parse_quote! { u16 }, + 1 => syn::parse_quote! { u8 }, _ => return None, }) } @@ -103,7 +105,7 @@ impl Opaque { pub(crate) fn known_rust_type_for_array( &self, ctx: &BindgenContext, - ) -> Option<&'static str> { + ) -> Option { Layout::known_type_for_size(ctx, self.0.align) } diff --git a/bindgen/ir/ty.rs b/bindgen/ir/ty.rs index 572d7de451..32bea702c1 100644 --- a/bindgen/ir/ty.rs +++ b/bindgen/ir/ty.rs @@ -5,7 +5,6 @@ use super::context::{BindgenContext, ItemId, TypeId}; use super::dot::DotAttributes; use super::enum_ty::Enum; use super::function::FunctionSig; -use super::int::IntKind; use super::item::{IsOpaque, Item}; use super::layout::{Layout, Opaque}; use super::objc::ObjCInterface; @@ -18,6 +17,8 @@ use crate::parse::{ParseError, ParseResult}; use std::borrow::Cow; use std::io; +pub use super::int::IntKind; + /// The base representation of a type in bindgen. /// /// A type has an optional name, which if present cannot be empty, a `layout`