diff --git a/bindgen-tests/tests/expectations/tests/complex_global.rs b/bindgen-tests/tests/expectations/tests/complex_global.rs index c818df676e..2dac56c7b5 100644 --- a/bindgen-tests/tests/expectations/tests/complex_global.rs +++ b/bindgen-tests/tests/expectations/tests/complex_global.rs @@ -1,5 +1,8 @@ #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] #[derive(PartialEq, Copy, Clone, Hash, Debug, Default)] +#[repr(C, align(16))] +pub struct __BindgenLongDouble([u8; 16]); +#[derive(PartialEq, Copy, Clone, Hash, Debug, Default)] #[repr(C)] pub struct __BindgenComplex { pub re: T, @@ -12,5 +15,5 @@ unsafe extern "C" { pub static mut globalValueDouble: __BindgenComplex; } unsafe extern "C" { - pub static mut globalValueLongDouble: __BindgenComplex; + pub static mut globalValueLongDouble: __BindgenComplex<__BindgenLongDouble>; } diff --git a/bindgen-tests/tests/expectations/tests/convert-floats-win64.rs b/bindgen-tests/tests/expectations/tests/convert-floats-win64.rs new file mode 100644 index 0000000000..3baa5782b6 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/convert-floats-win64.rs @@ -0,0 +1,41 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +#[derive(PartialEq, Copy, Clone, Hash, Debug, Default)] +#[repr(C)] +pub struct __BindgenComplex { + pub re: T, + pub im: T, +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct foo { + pub bar: f32, + pub baz: f32, + pub bazz: f64, + pub bazzz: *mut f64, + pub complexFloat: __BindgenComplex, + pub complexDouble: __BindgenComplex, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of foo"][::std::mem::size_of::() - 48usize]; + ["Alignment of foo"][::std::mem::align_of::() - 8usize]; + ["Offset of field: foo::bar"][::std::mem::offset_of!(foo, bar) - 0usize]; + ["Offset of field: foo::baz"][::std::mem::offset_of!(foo, baz) - 4usize]; + ["Offset of field: foo::bazz"][::std::mem::offset_of!(foo, bazz) - 8usize]; + ["Offset of field: foo::bazzz"][::std::mem::offset_of!(foo, bazzz) - 16usize]; + [ + "Offset of field: foo::complexFloat", + ][::std::mem::offset_of!(foo, complexFloat) - 24usize]; + [ + "Offset of field: foo::complexDouble", + ][::std::mem::offset_of!(foo, complexDouble) - 32usize]; +}; +impl Default for foo { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} diff --git a/bindgen-tests/tests/expectations/tests/convert-floats.rs b/bindgen-tests/tests/expectations/tests/convert-floats.rs index 9ca939f7c5..e1f109945d 100644 --- a/bindgen-tests/tests/expectations/tests/convert-floats.rs +++ b/bindgen-tests/tests/expectations/tests/convert-floats.rs @@ -1,5 +1,8 @@ #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] #[derive(PartialEq, Copy, Clone, Hash, Debug, Default)] +#[repr(C, align(16))] +pub struct __BindgenLongDouble([u8; 16]); +#[derive(PartialEq, Copy, Clone, Hash, Debug, Default)] #[repr(C)] pub struct __BindgenComplex { pub re: T, @@ -11,7 +14,7 @@ pub struct foo { pub bar: ::std::os::raw::c_float, pub baz: ::std::os::raw::c_float, pub bazz: ::std::os::raw::c_double, - pub bazzz: *mut u128, + pub bazzz: *mut __BindgenLongDouble, pub complexFloat: __BindgenComplex<::std::os::raw::c_float>, pub complexDouble: __BindgenComplex<::std::os::raw::c_double>, } diff --git a/bindgen-tests/tests/expectations/tests/long_double.rs b/bindgen-tests/tests/expectations/tests/long_double.rs index aa3109ca43..3ceee9047f 100644 --- a/bindgen-tests/tests/expectations/tests/long_double.rs +++ b/bindgen-tests/tests/expectations/tests/long_double.rs @@ -1,9 +1,12 @@ #![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +#[derive(PartialEq, Copy, Clone, Hash, Debug, Default)] +#[repr(C, align(16))] +pub struct __BindgenLongDouble([u8; 16]); #[repr(C)] #[repr(align(16))] #[derive(Debug, Default, Copy, Clone)] pub struct foo { - pub bar: u128, + pub bar: __BindgenLongDouble, } #[test] fn bindgen_test_layout_foo() { diff --git a/bindgen-tests/tests/headers/convert-floats-win64.h b/bindgen-tests/tests/headers/convert-floats-win64.h new file mode 100644 index 0000000000..4696f57d5d --- /dev/null +++ b/bindgen-tests/tests/headers/convert-floats-win64.h @@ -0,0 +1,9 @@ +// bindgen-flags: -- --target=x86_64-pc-windows-msvc + +struct foo { + float bar, baz; + double bazz; + long double* bazzz; + float _Complex complexFloat; + double _Complex complexDouble; +}; diff --git a/bindgen-tests/tests/headers/convert-floats.h b/bindgen-tests/tests/headers/convert-floats.h index 08d9fe0bd2..0abed46ea0 100644 --- a/bindgen-tests/tests/headers/convert-floats.h +++ b/bindgen-tests/tests/headers/convert-floats.h @@ -1,4 +1,4 @@ -// bindgen-flags: --no-convert-floats +// bindgen-flags: --no-convert-floats -- --target=x86_64-unknown-linux-gnu struct foo { float bar, baz; diff --git a/bindgen-tests/tests/headers/long_double.h b/bindgen-tests/tests/headers/long_double.h index c8872d6ebf..73ced5d858 100644 --- a/bindgen-tests/tests/headers/long_double.h +++ b/bindgen-tests/tests/headers/long_double.h @@ -3,3 +3,5 @@ struct foo { long double bar; }; + +void take_ld(long double ld); diff --git a/bindgen/codegen/helpers.rs b/bindgen/codegen/helpers.rs index 82172f3488..6d640c37ca 100644 --- a/bindgen/codegen/helpers.rs +++ b/bindgen/codegen/helpers.rs @@ -243,14 +243,19 @@ pub(crate) mod ast_ty { pub(crate) fn float_kind_rust_type( ctx: &BindgenContext, fk: FloatKind, - layout: Option, + mut layout: Option, + n_parts: usize, ) -> 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::Float16, _) => { + // Convert the layout of complex numbers to the layout of its parts. + let bits = match layout { + Some(Layout { ref mut size, .. }) => { + *size /= n_parts; + Some(*size * 8) + } + None => None, + }; + match (fk, ctx.options().convert_floats, bits) { + (FloatKind::Float16, _, _) => { // TODO: do f16 when rust lands it ctx.generated_bindgen_float16(); if ctx.options().enable_cxx_namespaces { @@ -259,29 +264,29 @@ pub(crate) mod ast_ty { syn::parse_quote! { __BindgenFloat16 } } } - (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, _) => { - if let Some(layout) = layout { - match layout.size { - 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(layout) - .unwrap_or(syn::parse_quote! { f64 }), + (_, true, Some(32)) => syn::parse_quote! { f32 }, + (_, true, Some(64)) | (FloatKind::Double, true, None) => { + syn::parse_quote! { f64 } + } + (FloatKind::Float, ..) => raw_type(ctx, "c_float"), + (FloatKind::Double, ..) => raw_type(ctx, "c_double"), + (FloatKind::LongDouble, ..) => { + // TODO(emilio): If rust ever gains f80/f128 we should + // use it here and below. + ctx.generated_bindgen_long_double( + layout.expect("unknown layout for long double"), + ); + if ctx.options().enable_cxx_namespaces { + syn::parse_quote! { + root::__BindgenLongDouble } } else { - debug_assert!( - false, - "How didn't we know the layout for a primitive type?" - ); - syn::parse_quote! { f64 } + syn::parse_quote! { + __BindgenLongDouble + } } } - (FloatKind::Float128, _) => { + (FloatKind::Float128, ..) => { if true { syn::parse_quote! { u128 } } else { diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index f5518e432d..55b0b63e17 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -584,6 +584,9 @@ impl CodeGenerator for Module { if ctx.need_opaque_array_type() { utils::prepend_opaque_array_type(&mut *result); } + if let Some(layout) = ctx.need_bindgen_long_double() { + utils::prepend_long_double(layout, &mut *result); + } if result.saw_objc { utils::prepend_objc_header(ctx, &mut *result); } @@ -3043,6 +3046,10 @@ impl Method { return; } + if utils::sig_unsupported_types(ctx, signature) { + 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() { @@ -4287,11 +4294,11 @@ impl TryToRustTy for Type { Ok(int_kind_rust_type(ctx, ik, self.layout(ctx))) } TypeKind::Float(fk) => { - Ok(float_kind_rust_type(ctx, fk, self.layout(ctx))) + Ok(float_kind_rust_type(ctx, fk, self.layout(ctx), 1)) } TypeKind::Complex(fk) => { let float_path = - float_kind_rust_type(ctx, fk, self.layout(ctx)); + float_kind_rust_type(ctx, fk, self.layout(ctx), 2); ctx.generated_bindgen_complex(); Ok(if ctx.options().enable_cxx_namespaces { @@ -4644,6 +4651,10 @@ impl CodeGenerator for Function { Ok(abi) => abi, }; + if utils::sig_unsupported_types(ctx, signature) { + return None; + } + // Handle overloaded functions by giving each overload its own unique // suffix. let times_seen = result.overload_number(&canonical_name); @@ -5193,6 +5204,7 @@ pub(crate) mod utils { use crate::ir::context::TypeId; use crate::ir::function::{Abi, ClangAbi, FunctionSig}; use crate::ir::item::{Item, ItemCanonicalPath}; + use crate::ir::layout::Layout; use crate::ir::ty::TypeKind; use crate::{args_are_cpp, file_is_cpp}; use std::borrow::Cow; @@ -5627,6 +5639,23 @@ pub(crate) mod utils { result.insert(0, ty); } + pub(crate) fn prepend_long_double( + layout: Layout, + result: &mut Vec, + ) { + let Layout { align, size, .. } = layout; + let align = proc_macro2::Literal::u64_unsuffixed(align as u64); + let size = proc_macro2::Literal::u64_unsuffixed(size as u64); + result.insert( + 0, + quote! { + #[derive(PartialEq, Copy, Clone, Hash, Debug, Default)] + #[repr(C, align(#align))] + pub struct __BindgenLongDouble([u8; #size]); + }, + ); + } + pub(crate) fn build_path( item: &Item, ctx: &BindgenContext, @@ -5918,4 +5947,15 @@ pub(crate) mod utils { true } + + pub(crate) fn sig_unsupported_types( + ctx: &BindgenContext, + sig: &FunctionSig, + ) -> bool { + sig.argument_types() + .iter() + .map(|(_, ty_id)| ty_id) + .chain(std::iter::once(&sig.return_type())) + .any(|ty_id| ctx.lookup_never_by_value(*ty_id)) + } } diff --git a/bindgen/ir/analysis/mod.rs b/bindgen/ir/analysis/mod.rs index 74a305edfb..5e22018974 100644 --- a/bindgen/ir/analysis/mod.rs +++ b/bindgen/ir/analysis/mod.rs @@ -53,6 +53,8 @@ mod has_type_param_in_array; pub(crate) use self::has_type_param_in_array::HasTypeParameterInArray; mod has_float; pub(crate) use self::has_float::HasFloat; +mod never_by_value; +pub use self::never_by_value::NeverByValue; mod sizedness; pub(crate) use self::sizedness::{ Sizedness, SizednessAnalysis, SizednessResult, diff --git a/bindgen/ir/analysis/never_by_value.rs b/bindgen/ir/analysis/never_by_value.rs new file mode 100644 index 0000000000..d3b37defff --- /dev/null +++ b/bindgen/ir/analysis/never_by_value.rs @@ -0,0 +1,241 @@ +//! Determining which types cannot be passed by value. + +use super::{generate_dependencies, ConstrainResult, MonotoneFramework}; +use crate::ir::comp::Field; +use crate::ir::comp::FieldMethods; +use crate::ir::context::{BindgenContext, ItemId}; +use crate::ir::traversal::EdgeKind; +use crate::ir::ty::TypeKind; +use crate::{HashMap, HashSet}; + +/// An analysis that finds each IR item which is a type which cannot be passed by value in Rust. +/// +/// This is defined as follows: +/// +/// * If T is float or complex float with size not 32 or 64 bit (twice that for complex), it cannot +/// be passed by value. +/// * If T is a type alias, a templated alias or an indirection to another type, +/// it matches if the type T refers to does +/// * If T is a compound type, it matches if any of base memter or field does. +/// * If T is an instantiation of an abstract template definition, T matches if any of the template +/// arguments or template definition do. +/// +/// A more advanced implementation might be aware of which registers arguments will actually end up +/// in and permit some signatures this rejects on a platform-specific basis. +#[derive(Debug, Clone)] +pub struct NeverByValue<'ctx> { + ctx: &'ctx BindgenContext, + + // The incremental result of this analysis's computation. Everything in this + // set has float. + never_by_value: HashSet, + + // Dependencies saying that if a key ItemId has been inserted into the + // `never_by_value` set, then each of the ids in Vec need to be + // considered again. + // + // This is a subset of the natural IR graph with reversed edges, where we + // only include the edges from the IR graph that can affect whether a type + // has float or not. + dependencies: HashMap>, +} + +impl<'ctx> NeverByValue<'ctx> { + fn consider_edge(kind: EdgeKind) -> bool { + match kind { + EdgeKind::BaseMember | + EdgeKind::Field | + EdgeKind::TypeReference | + EdgeKind::VarType | + EdgeKind::TemplateArgument | + EdgeKind::TemplateDeclaration | + EdgeKind::TemplateParameterDefinition => true, + + EdgeKind::Constructor | + EdgeKind::Destructor | + EdgeKind::FunctionReturn | + EdgeKind::FunctionParameter | + EdgeKind::InnerType | + EdgeKind::InnerVar | + EdgeKind::Method => false, + EdgeKind::Generic => false, + } + } + + fn insert>(&mut self, id: Id) -> ConstrainResult { + let id = id.into(); + trace!("inserting {id:?} into the never_by_value set"); + + let was_not_already_in_set = self.never_by_value.insert(id); + assert!( + was_not_already_in_set, + "We shouldn't try and insert {:?} twice because if it was \ + already in the set, `constrain` should have exited early.", + id + ); + + ConstrainResult::Changed + } +} + +impl<'ctx> MonotoneFramework for NeverByValue<'ctx> { + type Node = ItemId; + type Extra = &'ctx BindgenContext; + type Output = HashSet; + + fn new(ctx: &'ctx BindgenContext) -> Self { + let never_by_value = HashSet::default(); + let dependencies = generate_dependencies(ctx, Self::consider_edge); + + NeverByValue { + ctx, + never_by_value, + dependencies, + } + } + + fn initial_worklist(&self) -> Vec { + self.ctx.allowlisted_items().iter().cloned().collect() + } + + fn constrain(&mut self, id: ItemId) -> ConstrainResult { + trace!("constrain: {id:?}"); + + if self.never_by_value.contains(&id) { + trace!(" already in set"); + return ConstrainResult::Same; + } + + let item = self.ctx.resolve_item(id); + if let Some(ty) = item.kind().as_type() { + match *ty.kind() { + TypeKind::Void | + TypeKind::NullPtr | + TypeKind::Int(..) | + TypeKind::Function(..) | + TypeKind::Enum(..) | + TypeKind::Reference(..) | + TypeKind::TypeParam | + TypeKind::Opaque | + TypeKind::Pointer(..) | + TypeKind::UnresolvedTypeRef(..) | + TypeKind::ObjCInterface(..) | + TypeKind::ObjCId | + TypeKind::ObjCSel | + TypeKind::BlockPointer(_) => { + trace!(" simple type that is not float"); + ConstrainResult::Same + } + + TypeKind::Float(..) | TypeKind::Complex(..) => { + if let Some(layout) = ty.layout(self.ctx) { + match (ty.kind(), layout.size) { + (TypeKind::Float(..), 4 | 8) | + (TypeKind::Complex(..), 8 | 16) => { + trace!(" skipped f32 or f64"); + ConstrainResult::Same + } + _ => { + trace!( + " extended float size {}", + layout.size + ); + self.insert(id) + } + } + } else { + // This case comes up with macro constants and doesn't seem relevant. + trace!(" unknown float"); + ConstrainResult::Same + } + } + + TypeKind::Alias(t) | + TypeKind::Array(t, _) | + TypeKind::ResolvedTypeRef(t) | + TypeKind::TemplateAlias(t, _) | + TypeKind::Vector(t, _) => { + if self.never_by_value.contains(&t.into()) { + trace!( + " contains/aliases matching type, so matches" + ); + self.insert(id) + } else { + trace!(" does not contain/alias matching type"); + ConstrainResult::Same + } + } + + TypeKind::Comp(ref info) => { + let bases_have = info.base_members().iter().any(|base| { + self.never_by_value.contains(&base.ty.into()) + }); + if bases_have { + trace!(" bases have float, so we also have"); + return self.insert(id); + } + let fields_have = info.fields().iter().any(|f| match *f { + Field::DataMember(ref data) => { + self.never_by_value.contains(&data.ty().into()) + } + Field::Bitfields(ref bfu) => { + bfu.bitfields().iter().any(|b| { + self.never_by_value.contains(&b.ty().into()) + }) + } + }); + if fields_have { + trace!(" fields have float, so we also have"); + return self.insert(id); + } + + trace!(" comp doesn't have float"); + ConstrainResult::Same + } + + TypeKind::TemplateInstantiation(ref template) => { + let args_have = template + .template_arguments() + .iter() + .any(|arg| self.never_by_value.contains(&arg.into())); + if args_have { + trace!(" template args match, so instantiation also matches"); + return self.insert(id); + } + + let def_has = self + .never_by_value + .contains(&template.template_definition().into()); + if def_has { + trace!(" template definition has float, so instantiation also has"); + return self.insert(id); + } + + trace!(" template instantiation does not match"); + ConstrainResult::Same + } + } + } else { + trace!(" not a type; skipped"); + ConstrainResult::Same + } + } + + fn each_depending_on(&self, id: ItemId, mut f: F) + where + F: FnMut(ItemId), + { + if let Some(edges) = self.dependencies.get(&id) { + for item in edges { + trace!("enqueue {item:?} into worklist"); + f(*item); + } + } + } +} + +impl<'ctx> From> for HashSet { + fn from(analysis: NeverByValue<'ctx>) -> Self { + analysis.never_by_value + } +} diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs index 99c75d63d8..77cbc2ea42 100644 --- a/bindgen/ir/context.rs +++ b/bindgen/ir/context.rs @@ -4,8 +4,8 @@ use super::super::time::Timer; use super::analysis::{ analyze, as_cannot_derive_set, CannotDerive, DeriveTrait, HasDestructorAnalysis, HasFloat, HasTypeParameterInArray, - HasVtableAnalysis, HasVtableResult, SizednessAnalysis, SizednessResult, - UsedTemplateParameters, + HasVtableAnalysis, HasVtableResult, NeverByValue, SizednessAnalysis, + SizednessResult, UsedTemplateParameters, }; use super::derive::{ CanDerive, CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveEq, @@ -21,6 +21,7 @@ use super::traversal::{self, Edge, ItemTraversal}; use super::ty::{FloatKind, Type, TypeKind}; use crate::clang::{self, ABIKind, Cursor}; use crate::codegen::CodegenError; +use crate::ir::layout::Layout; use crate::BindgenOptions; use crate::{Entry, HashMap, HashSet}; @@ -395,6 +396,9 @@ pub(crate) struct BindgenContext { /// Whether a bindgen float16 was generated generated_bindgen_float16: Cell, + /// Whether a bindgen long double was generated + generated_bindgen_long_double: Cell>, + /// The set of `ItemId`s that are allowlisted. This the very first thing /// computed after parsing our IR, and before running any of our analyses. allowlisted: Option, @@ -458,7 +462,7 @@ pub(crate) struct BindgenContext { /// and is always `None` before that and `Some` after. cannot_derive_hash: Option>, - /// The map why specified `ItemId`s of) types that can't derive hash. + /// The map why specified `ItemId`s of types that can't derive hash. /// /// This is populated when we enter codegen by /// `compute_cannot_derive_partialord_partialeq_or_eq` and is always `None` @@ -471,29 +475,37 @@ pub(crate) struct BindgenContext { /// that function is invoked and `Some` afterwards. sizedness: Option>, - /// The set of (`ItemId's of`) types that has vtable. + /// The set of (`ItemId`s of) types that have vtable. /// /// Populated when we enter codegen by `compute_has_vtable`; always `None` /// before that and `Some` after. have_vtable: Option>, - /// The set of (`ItemId's of`) types that has destructor. + /// The set of (`ItemId`s of) types that have destructor. /// /// Populated when we enter codegen by `compute_has_destructor`; always `None` /// before that and `Some` after. have_destructor: Option>, - /// The set of (`ItemId's of`) types that has array. + /// The set of (`ItemId`s of) types that have array. /// /// Populated when we enter codegen by `compute_has_type_param_in_array`; always `None` /// before that and `Some` after. has_type_param_in_array: Option>, - /// The set of (`ItemId's of`) types that has float. + /// The set of (`ItemId`s of) types that have float. /// /// Populated when we enter codegen by `compute_has_float`; always `None` /// before that and `Some` after. has_float: Option>, + + /// The set of (`ItemId` of) types that cannot be reliably passed by value. + /// + /// These are or contain an extended float, which cannot be represented correctly for passing + /// by value in Rust. (In many contexts, anything of the right size and alignment will do, but + /// calling conventions depend on specifying the actual type.) The definition of extended float + /// used is simply not being 32 or 64 bits wide. + never_by_value: Option>, } /// A traversal of allowlisted items. @@ -597,6 +609,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" generated_bindgen_complex: Cell::new(false), generated_bindgen_float16: Cell::new(false), generated_opaque_array: Cell::new(false), + generated_bindgen_long_double: Cell::new(None), allowlisted: None, blocklisted_types_implement_traits: Default::default(), codegen_items: None, @@ -613,6 +626,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" have_destructor: None, has_type_param_in_array: None, has_float: None, + never_by_value: None, } } @@ -1208,6 +1222,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.compute_cannot_derive_copy(); self.compute_has_type_param_in_array(); self.compute_has_float(); + self.compute_never_by_value(); self.compute_cannot_derive_hash(); self.compute_cannot_derive_partialord_partialeq_or_eq(); @@ -2618,6 +2633,20 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.generated_bindgen_float16.get() } + /// Call if a bindgen long double is generated + pub fn generated_bindgen_long_double(&self, layout: Layout) { + if let Some(old) = + self.generated_bindgen_long_double.replace(Some(layout)) + { + assert_eq!(layout, old, "long doubles had different layout"); + } + } + + /// Whether we need to generate the bindgen long double type (and if so, get its layout) + pub fn need_bindgen_long_double(&self) -> Option { + self.generated_bindgen_long_double.get() + } + /// Compute which `enum`s have an associated `typedef` definition. fn compute_enum_typedef_combos(&mut self) { let _t = self.timer("compute_enum_typedef_combos"); @@ -2887,6 +2916,18 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.has_float.as_ref().unwrap().contains(&id.into()) } + /// Compute whether the type has float. + fn compute_never_by_value(&mut self) { + let _t = self.timer("compute_never_by_value"); + assert!(self.never_by_value.is_none()); + self.never_by_value = Some(analyze::(self)); + } + + /// Look up whether the item with `id` may never be passed by value. + pub fn lookup_never_by_value>(&self, id: Id) -> bool { + self.never_by_value.as_ref().unwrap().contains(&id.into()) + } + /// Check if `--no-partialeq` flag is enabled for this item. pub(crate) fn no_partialeq_by_name(&self, item: &Item) -> bool { let name = item.path_for_allowlisting(self)[1..].join("::");