From 87601a33b36a64ea4e3c570ba0b2257ada52383a Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Fri, 17 May 2024 14:54:43 -0700 Subject: [PATCH 1/2] implement transparent typedef feature --- docs.md | 103 +++++++++-- src/bindgen/builder.rs | 2 +- src/bindgen/ir/constant.rs | 16 +- src/bindgen/ir/enumeration.rs | 38 ++-- src/bindgen/ir/function.rs | 23 ++- src/bindgen/ir/generic_path.rs | 67 ++++++- src/bindgen/ir/global.rs | 16 +- src/bindgen/ir/item.rs | 12 +- src/bindgen/ir/opaque.rs | 33 ++++ src/bindgen/ir/structure.rs | 27 ++- src/bindgen/ir/ty.rs | 172 ++++++++++++------ src/bindgen/ir/typedef.rs | 35 +++- src/bindgen/ir/union.rs | 21 ++- src/bindgen/library.rs | 82 +++++---- src/bindgen/parser.rs | 108 +++++++---- tests/expectations/const_transparent.cpp | 4 +- tests/expectations/transparent.c | 37 +++- tests/expectations/transparent.compat.c | 37 +++- tests/expectations/transparent.cpp | 37 +++- tests/expectations/transparent.pyx | 38 +++- tests/expectations/transparent_both.c | 37 +++- tests/expectations/transparent_both.compat.c | 37 +++- tests/expectations/transparent_tag.c | 37 +++- tests/expectations/transparent_tag.compat.c | 37 +++- tests/expectations/transparent_tag.pyx | 38 +++- tests/expectations/transparent_typedef.c | 87 +++++++++ .../expectations/transparent_typedef.compat.c | 95 ++++++++++ tests/expectations/transparent_typedef.cpp | 73 ++++++++ tests/expectations/transparent_typedef.pyx | 82 +++++++++ tests/expectations/transparent_typedef_both.c | 87 +++++++++ .../transparent_typedef_both.compat.c | 95 ++++++++++ tests/expectations/transparent_typedef_tag.c | 87 +++++++++ .../transparent_typedef_tag.compat.c | 95 ++++++++++ .../expectations/transparent_typedef_tag.pyx | 82 +++++++++ tests/rust/const_transparent.rs | 1 + tests/rust/transparent.rs | 70 ++++++- tests/rust/transparent_typedef.rs | 82 +++++++++ 37 files changed, 1817 insertions(+), 213 deletions(-) create mode 100644 tests/expectations/transparent_typedef.c create mode 100644 tests/expectations/transparent_typedef.compat.c create mode 100644 tests/expectations/transparent_typedef.cpp create mode 100644 tests/expectations/transparent_typedef.pyx create mode 100644 tests/expectations/transparent_typedef_both.c create mode 100644 tests/expectations/transparent_typedef_both.compat.c create mode 100644 tests/expectations/transparent_typedef_tag.c create mode 100644 tests/expectations/transparent_typedef_tag.compat.c create mode 100644 tests/expectations/transparent_typedef_tag.pyx create mode 100644 tests/rust/transparent_typedef.rs diff --git a/docs.md b/docs.md index c8ce9f4f2..ad5dcc188 100644 --- a/docs.md +++ b/docs.md @@ -299,7 +299,76 @@ fn bar() -> Foo { .. } // Will be emitted as `struct foo bar();` ### Struct Annotations -* field-names=\[field1, field2, ...\] -- sets the names of all the fields in the output struct. These names will be output verbatim, and are not eligible for renaming. +* field-names=\[field1, field2, ...\] -- sets the names of all the fields in the output + struct. These names will be output verbatim, and are not eligible for renaming. + +* transparent-typedef -- when emitting the typedef for a transparent struct, mark it as + transparent. All references to the struct will be replaced with the type of its underlying NZST + field, effectively making the struct invisible on the FFI side. For example, consider the + following Rust code: + + ```rust + #[repr(transparent)] + pub struct Handle { + ptr: NonNull, + } + + pub struct Foo { } + + #[no_mangle] + pub extern "C" fn foo_operation(foo: Option>) { } + ``` + + By default, the exported C++ code would fail to compile, because the function takes `Option<...>` + (which is an opaque type) by value: + + ```cpp + template + struct Option; + + template + using Handle = T; + + struct Foo; + + void foo_operation(Option> foo); + ``` + + If we annotate `Handle` with `transparent-typedef` (leaving the rest of the code unchanged): + ```rust + /// cbindgen:transparent-typedef + #[repr(transparent)] + pub struct Handle { + ptr: NonNull, + } + ``` + + Then cbindgen is able to simplify the exported C++ code to just: + ```cpp + struct Foo; + + void foo_operation(Foo* foo); + ``` + + NOTE: This annotation does _NOT_ affect user-defined type aliases for transparent structs. If we + we adjust the previous example to use a type alias: + + ```rust + type NullableFooHandle = Option>; + + #[no_mangle] + pub extern "C" fn foo_operation(foo: NullableFooHandle) { } + ``` + + Then the exported code will use it as expected: + + ```cpp + struct Foo; + + using NullableFooHandle = Foo*; + + void foo_operation(NullableFooHandle foo); + ``` The rest are just local overrides for the same options found in the cbindgen.toml: @@ -316,27 +385,25 @@ The rest are just local overrides for the same options found in the cbindgen.tom / etc(if any). The idea is for this to be used to annotate the operator with attributes, for example: -```rust -/// cbindgen:eq-attributes=MY_ATTRIBUTES -#[repr(C)] -pub struct Foo { .. } -``` - -Will generate something like: + ```rust + /// cbindgen:eq-attributes=MY_ATTRIBUTES + #[repr(C)] + pub struct Foo { .. } + ``` -``` - MY_ATTRIBUTES bool operator==(const Foo& other) const { - ... - } -``` + Will generate something like: -Combined with something like: + ``` + MY_ATTRIBUTES bool operator==(const Foo& other) const { + ... + } + ``` -``` -#define MY_ATTRIBUTES [[nodiscard]] -``` + Combined with something like: -for example. + ``` + #define MY_ATTRIBUTES [[nodiscard]] + ``` ### Enum Annotations diff --git a/src/bindgen/builder.rs b/src/bindgen/builder.rs index d47919b98..a649a0fc0 100644 --- a/src/bindgen/builder.rs +++ b/src/bindgen/builder.rs @@ -368,7 +368,7 @@ impl Builder { let mut result = Parse::new(); if self.std_types { - result.add_std_types(); + result.add_std_types(self.config.language); } for x in &self.srcs { diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs index 2f1d3bffd..c820b2a41 100644 --- a/src/bindgen/ir/constant.rs +++ b/src/bindgen/ir/constant.rs @@ -13,8 +13,8 @@ use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ - AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path, - Struct, ToCondition, Type, + AnnotationSet, Cfg, ConditionWrite, Documentation, GenericArgument, GenericParams, Item, + ItemContainer, Path, Struct, ToCondition, TransparentTypeEraser, Type, }; use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::library::Library; @@ -603,6 +603,18 @@ impl Item for Constant { fn generic_params(&self) -> &GenericParams { GenericParams::empty() } + + fn erase_transparent_types_inplace( + &mut self, + library: &Library, + eraser: &mut TransparentTypeEraser, + _generics: &[GenericArgument], + ) { + // NOTE: We also need to simplify the literal initializer value to match the underlying + // type, but that is true for all transparent structs (not just transparent-typedef + // structs), and is handled by the `write` method below. + eraser.erase_transparent_types_inplace(library, &mut self.ty, &[]); + } } impl Constant { diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs index 2e633a7df..f5a0bbeed 100644 --- a/src/bindgen/ir/enumeration.rs +++ b/src/bindgen/ir/enumeration.rs @@ -12,7 +12,7 @@ use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ AnnotationSet, AnnotationValue, Cfg, ConditionWrite, DeprecatedNoteKind, Documentation, Field, GenericArgument, GenericParams, GenericPath, Item, ItemContainer, Literal, Path, Repr, - ReprStyle, Struct, ToCondition, Type, + ReprStyle, Struct, ToCondition, TransparentTypeEraser, Type, }; use crate::bindgen::language_backend::LanguageBackend; use crate::bindgen::library::Library; @@ -247,12 +247,6 @@ impl EnumVariant { } } - fn simplify_standard_types(&mut self, config: &Config) { - if let VariantBody::Body { ref mut body, .. } = self.body { - body.simplify_standard_types(config); - } - } - fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { if let VariantBody::Body { ref body, .. } = self.body { body.add_dependencies(library, out); @@ -500,6 +494,30 @@ impl Item for Enum { &self.generic_params } + fn erase_transparent_types_inplace( + &mut self, + library: &Library, + eraser: &mut TransparentTypeEraser, + generics: &[GenericArgument], + ) { + let mut skip_inline_tag_field = Self::inline_tag_field(&self.repr); + let generics = self.generic_params.defaulted_generics(generics); + let mappings = self.generic_params.call(self.name(), &generics); + for variant in self.variants.iter_mut() { + if let VariantBody::Body { ref mut body, .. } = variant.body { + for field in body.fields.iter_mut() { + // Ignore the inline Tag field, if any (it's always first) + if skip_inline_tag_field { + debug!("Skipping inline Tag field {:?}", field); + skip_inline_tag_field = false; + } else { + eraser.erase_transparent_types_inplace(library, &mut field.ty, &mappings); + } + } + } + } + } + fn rename_for_config(&mut self, config: &Config) { config.export.rename(&mut self.export_name); @@ -1493,10 +1511,4 @@ impl Enum { } } } - - pub fn simplify_standard_types(&mut self, config: &Config) { - for variant in &mut self.variants { - variant.simplify_standard_types(config); - } - } } diff --git a/src/bindgen/ir/function.rs b/src/bindgen/ir/function.rs index 79adfce94..129f84383 100644 --- a/src/bindgen/ir/function.rs +++ b/src/bindgen/ir/function.rs @@ -9,7 +9,9 @@ use syn::ext::IdentExt; use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; -use crate::bindgen::ir::{AnnotationSet, Cfg, Documentation, GenericPath, Path, Type}; +use crate::bindgen::ir::{ + AnnotationSet, Cfg, Documentation, GenericPath, Path, TransparentTypeEraser, Type, +}; use crate::bindgen::library::Library; use crate::bindgen::monomorph::Monomorphs; use crate::bindgen::rename::{IdentifierType, RenameRule}; @@ -115,13 +117,6 @@ impl Function { &self.path } - pub fn simplify_standard_types(&mut self, config: &Config) { - self.ret.simplify_standard_types(config); - for arg in &mut self.args { - arg.ty.simplify_standard_types(config); - } - } - pub fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { self.ret.add_dependencies(library, out); for arg in &self.args { @@ -150,6 +145,18 @@ impl Function { } } + // NOTE: No `generics` arg because Functions do not support generics and do not `impl Item`. + pub fn erase_transparent_types_inplace( + &mut self, + library: &Library, + eraser: &mut TransparentTypeEraser, + ) { + eraser.erase_transparent_types_inplace(library, &mut self.ret, &[]); + for arg in &mut self.args { + eraser.erase_transparent_types_inplace(library, &mut arg.ty, &[]); + } + } + pub fn rename_for_config(&mut self, config: &Config) { // Rename the types used in arguments let generic_params = Default::default(); diff --git a/src/bindgen/ir/generic_path.rs b/src/bindgen/ir/generic_path.rs index 4610641ce..8231347f0 100644 --- a/src/bindgen/ir/generic_path.rs +++ b/src/bindgen/ir/generic_path.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; +use std::collections::HashMap; use std::io::Write; use std::ops::Deref; @@ -8,16 +10,17 @@ use crate::bindgen::config::{Config, Language}; use crate::bindgen::declarationtyperesolver::{DeclarationType, DeclarationTypeResolver}; use crate::bindgen::ir::{ConstExpr, Path, Type}; use crate::bindgen::language_backend::LanguageBackend; +use crate::bindgen::library::Library; use crate::bindgen::utilities::IterHelpers; use crate::bindgen::writer::SourceWriter; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum GenericParamType { Type, Const(Type), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct GenericParam { name: Path, ty: GenericParamType, @@ -103,6 +106,24 @@ impl GenericParams { Ok(GenericParams(params)) } + /// If `generics` is empty, create a set of "default" generic arguments, which preserves the + /// existing parameter name. Useful to allow `call` to work when no generics are provided. + pub fn defaulted_generics<'a>( + &self, + generics: &'a [GenericArgument], + ) -> Cow<'a, [GenericArgument]> { + if !self.is_empty() && generics.is_empty() { + Cow::Owned( + self.iter() + .map(|param| Type::Path(GenericPath::new(param.name.clone(), vec![]))) + .map(GenericArgument::Type) + .collect(), + ) + } else { + Cow::Borrowed(generics) + } + } + /// Associate each parameter with an argument. pub fn call<'out>( &'out self, @@ -234,6 +255,48 @@ impl GenericArgument { } } +/// Helper for erasing transparent types, which memoizes already-seen types to avoid repeated work. +#[derive(Default)] +pub struct TransparentTypeEraser { + // Remember paths we've already visited, so we don't repeat unnecessary work. + // TODO: how to handle recursive types such as `struct Foo { next: Box }`? + known_types: HashMap>, +} + +impl TransparentTypeEraser { + pub fn erase_transparent_types_inplace( + &mut self, + library: &Library, + target: &mut Type, + mappings: &[(&Path, &GenericArgument)], + ) { + if let Some(erased_type) = self.erase_transparent_types(library, target, mappings) { + *target = erased_type; + } + } + + #[must_use] + pub fn erase_transparent_types( + &mut self, + library: &Library, + target: &Type, + mappings: &[(&Path, &GenericArgument)], + ) -> Option { + let known_type = self.known_types.get(target); + let unknown_type = known_type.is_none(); + let erased_type = if let Some(ty) = known_type { + ty.clone() + } else { + target.erase_transparent_types(library, mappings, self) + }; + if unknown_type { + debug!("Caching erasure of {:?} as {:?}", target, erased_type); + self.known_types.insert(target.clone(), erased_type.clone()); + } + erased_type + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct GenericPath { path: Path, diff --git a/src/bindgen/ir/global.rs b/src/bindgen/ir/global.rs index 7816a294b..3d825eaa2 100644 --- a/src/bindgen/ir/global.rs +++ b/src/bindgen/ir/global.rs @@ -6,7 +6,8 @@ use crate::bindgen::config::Config; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ - AnnotationSet, Cfg, Documentation, GenericParams, Item, ItemContainer, Path, Type, + AnnotationSet, Cfg, Documentation, GenericArgument, GenericParams, Item, ItemContainer, Path, + TransparentTypeEraser, Type, }; use crate::bindgen::library::Library; @@ -62,10 +63,6 @@ impl Static { documentation, } } - - pub fn simplify_standard_types(&mut self, config: &Config) { - self.ty.simplify_standard_types(config); - } } impl Item for Static { @@ -109,6 +106,15 @@ impl Item for Static { GenericParams::empty() } + fn erase_transparent_types_inplace( + &mut self, + library: &Library, + eraser: &mut TransparentTypeEraser, + _generics: &[GenericArgument], + ) { + eraser.erase_transparent_types_inplace(library, &mut self.ty, &[]); + } + fn add_dependencies(&self, library: &Library, out: &mut Dependencies) { self.ty.add_dependencies(library, out); } diff --git a/src/bindgen/ir/item.rs b/src/bindgen/ir/item.rs index 03e1c153a..12ebfeb2f 100644 --- a/src/bindgen/ir/item.rs +++ b/src/bindgen/ir/item.rs @@ -10,7 +10,7 @@ use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ AnnotationSet, Cfg, Constant, Documentation, Enum, GenericArgument, GenericParams, OpaqueItem, - Path, Static, Struct, Typedef, Union, + Path, Static, Struct, TransparentTypeEraser, Typedef, Union, }; use crate::bindgen::library::Library; use crate::bindgen::monomorph::Monomorphs; @@ -39,6 +39,16 @@ pub trait Item { } fn generic_params(&self) -> &GenericParams; + /// Recursively erases any transparent types this item references. Type erasure impacts typedefs + /// and/or `#[repr(transparent)]` types annotated with `cbindgen:transparent-typedef`. It also + /// impacts certain built-in types (such as `Box`, `Option`, `Pin`, and `NonNull`). + fn erase_transparent_types_inplace( + &mut self, + library: &Library, + eraser: &mut TransparentTypeEraser, + generics: &[GenericArgument], + ); + fn is_generic(&self) -> bool { !self.generic_params().is_empty() } diff --git a/src/bindgen/ir/opaque.rs b/src/bindgen/ir/opaque.rs index 4527e3ca8..7db1918d7 100644 --- a/src/bindgen/ir/opaque.rs +++ b/src/bindgen/ir/opaque.rs @@ -7,6 +7,7 @@ use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ AnnotationSet, Cfg, Documentation, GenericArgument, GenericParams, Item, ItemContainer, Path, + TransparentTypeEraser, Type, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; @@ -55,6 +56,29 @@ impl OpaqueItem { documentation, } } + + // Most transparent well-known types are registered as transparent typedefs at parsing + // time. Some well-known generic types can be transparent, but with type-specific semantics that + // cannot be captured by a simple transparent typedef. The parser registers them as opaque items + // to be handled here. + pub fn as_transparent_alias(&self, generics: &[GenericArgument]) -> Option { + if let Some(GenericArgument::Type(ref ty)) = generics.first() { + match self.name() { + "NonNull" => { + return Some(Type::Ptr { + ty: Box::new(ty.clone()), + is_const: false, + is_nullable: false, + is_ref: false, + }) + } + "NonZero" => return ty.make_zeroable(false), + "Option" => return ty.make_nullable().or_else(|| ty.make_zeroable(true)), + _ => {} + } + } + None + } } impl Item for OpaqueItem { @@ -94,6 +118,15 @@ impl Item for OpaqueItem { &self.generic_params } + fn erase_transparent_types_inplace( + &mut self, + _library: &Library, + _erased: &mut TransparentTypeEraser, + _generics: &[GenericArgument], + ) { + // Nothing to do here, because we don't reference any types. + } + fn rename_for_config(&mut self, config: &Config) { config.export.rename(&mut self.export_name); } diff --git a/src/bindgen/ir/structure.rs b/src/bindgen/ir/structure.rs index 9b33a15c7..602c8a934 100644 --- a/src/bindgen/ir/structure.rs +++ b/src/bindgen/ir/structure.rs @@ -11,7 +11,7 @@ use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ AnnotationSet, Cfg, Constant, Documentation, Field, GenericArgument, GenericParams, Item, - ItemContainer, Path, Repr, ReprAlign, ReprStyle, Type, Typedef, + ItemContainer, Path, Repr, ReprAlign, ReprStyle, TransparentTypeEraser, Type, Typedef, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; @@ -160,12 +160,6 @@ impl Struct { } } - pub fn simplify_standard_types(&mut self, config: &Config) { - for field in &mut self.fields { - field.ty.simplify_standard_types(config); - } - } - /// Attempts to convert this struct to a typedef (only works for transparent structs). pub fn as_typedef(&self) -> Option { match self.fields.first() { @@ -174,6 +168,12 @@ impl Struct { } } + // Transparent structs become typedefs, so try converting to typedef and recurse on that. + pub fn as_transparent_alias(&self, generics: &[GenericArgument]) -> Option { + self.as_typedef() + .and_then(|t| t.as_transparent_alias(generics)) + } + pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) { // Generic structs can instantiate monomorphs only once they've been // instantiated. See `instantiate_monomorph` for more details. @@ -312,6 +312,19 @@ impl Item for Struct { &self.generic_params } + fn erase_transparent_types_inplace( + &mut self, + library: &Library, + eraser: &mut TransparentTypeEraser, + generics: &[GenericArgument], + ) { + let generics = self.generic_params.defaulted_generics(generics); + let mappings = self.generic_params.call(self.name(), &generics); + for field in &mut self.fields { + eraser.erase_transparent_types_inplace(library, &mut field.ty, &mappings); + } + } + fn rename_for_config(&mut self, config: &Config) { // Rename the name of the struct if !(self.has_tag_field && config.language == Language::Cxx) { diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs index 4a41d4cf3..c396eeda6 100644 --- a/src/bindgen/ir/ty.rs +++ b/src/bindgen/ir/ty.rs @@ -2,14 +2,14 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use std::borrow::Cow; - use syn::ext::IdentExt; -use crate::bindgen::config::{Config, Language}; +use crate::bindgen::config::Config; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; -use crate::bindgen::ir::{GenericArgument, GenericParams, GenericPath, Path}; +use crate::bindgen::ir::{ + GenericArgument, GenericParams, GenericPath, ItemContainer, Path, TransparentTypeEraser, +}; use crate::bindgen::library::Library; use crate::bindgen::monomorph::Monomorphs; use crate::bindgen::utilities::IterHelpers; @@ -519,59 +519,121 @@ impl Type { } } - fn simplified_type(&self, config: &Config) -> Option { - let path = match *self { - Type::Path(ref p) => p, - _ => return None, - }; - - if path.generics().is_empty() { - return None; - } - - if path.generics().len() != 1 { - return None; - } - - let unsimplified_generic = match path.generics()[0] { - GenericArgument::Type(ref ty) => ty, - GenericArgument::Const(_) => return None, - }; - - let generic = match unsimplified_generic.simplified_type(config) { - Some(generic) => Cow::Owned(generic), - None => Cow::Borrowed(unsimplified_generic), - }; - match path.name() { - "Option" => generic - .make_nullable() - .or_else(|| generic.make_zeroable(true)), - "NonNull" => Some(Type::Ptr { - ty: Box::new(generic.into_owned()), - is_const: false, - is_nullable: false, - is_ref: false, - }), - "NonZero" => generic.make_zeroable(false), - "Box" if config.language != Language::Cxx => Some(Type::Ptr { - ty: Box::new(generic.into_owned()), - is_const: false, - is_nullable: false, - is_ref: false, - }), - "Cell" => Some(generic.into_owned()), - "ManuallyDrop" | "MaybeUninit" | "Pin" if config.language != Language::Cxx => { - Some(generic.into_owned()) + #[must_use] + pub fn erase_transparent_types( + &self, + library: &Library, + mappings: &[(&Path, &GenericArgument)], + eraser: &mut TransparentTypeEraser, + ) -> Option { + match *self { + Type::Ptr { + ref ty, + is_const, + is_nullable, + is_ref, + } => { + if let Some(erased_type) = eraser.erase_transparent_types(library, ty, mappings) { + return Some(Type::Ptr { + ty: Box::new(erased_type), + is_const, + is_nullable, + is_ref, + }); + } + } + Type::Path(ref path) => { + if let Some(mut items) = library.get_items(path.path()) { + if let Some(item) = items.first_mut() { + // First, erase the generic args themselves. This is nice for most Item + // types, but crucial for `OpaqueItem` that would otherwise miss out. + let mut did_erase_generics = false; + let generics: Vec<_> = path + .generics() + .iter() + .map(|g| match g { + GenericArgument::Type(ty) => { + let erased_ty = + eraser.erase_transparent_types(library, ty, mappings); + if erased_ty.is_some() { + did_erase_generics = true; + }; + GenericArgument::Type(erased_ty.unwrap_or_else(|| ty.clone())) + } + other => other.clone(), + }) + .collect(); + + // Replace transparent items with their underlying type and erase it. + let aliased_ty = match item { + ItemContainer::OpaqueItem(o) => o.as_transparent_alias(&generics), + ItemContainer::Typedef(t) => t.as_transparent_alias(&generics), + ItemContainer::Struct(s) => s.as_transparent_alias(&generics), + _ => None, + }; + if let Some(mut ty) = aliased_ty { + eraser.erase_transparent_types_inplace(library, &mut ty, mappings); + return Some(ty); + } else if did_erase_generics { + // The type was not transparent, but some of its generics were erased. + let path = GenericPath::new(path.path().clone(), generics); + return Some(Type::Path(path)); + } + } + } else { + warn!( + "Can't find {}. This usually means that this type was incompatible or \ + not found.", + path.path() + ); + } + } + Type::Primitive(..) => {} // cannot simplify further + Type::Array(ref ty, ref constexpr) => { + if let Some(erased_ty) = eraser.erase_transparent_types(library, ty, mappings) { + return Some(Type::Array(Box::new(erased_ty), constexpr.clone())); + } + } + Type::FuncPtr { + ref ret, + ref args, + is_nullable, + never_return, + } => { + // Predict that ret+args will all be erased, but decrement the count whenever we're + // wrong. If the count drops to 0, then type erasure was a no-op after all. + let mut num_erased = 1 + args.len(); + let erased_ret = eraser + .erase_transparent_types(library, ret, mappings) + .unwrap_or_else(|| { + num_erased -= 1; + ret.as_ref().clone() + }); + + let erased_args = args + .iter() + .map(|(name, ty)| { + let erased_ty = eraser + .erase_transparent_types(library, ty, mappings) + .unwrap_or_else(|| { + num_erased -= 1; + ty.clone() + }); + (name.clone(), erased_ty) + }) + .collect(); + + if num_erased > 0 { + return Some(Type::FuncPtr { + ret: Box::new(erased_ret), + args: erased_args, + is_nullable, + never_return, + }); + } } - _ => None, - } - } - - pub fn simplify_standard_types(&mut self, config: &Config) { - self.visit_types(|ty| ty.simplify_standard_types(config)); - if let Some(ty) = self.simplified_type(config) { - *self = ty; } + None } pub fn replace_self_with(&mut self, self_ty: &Path) { diff --git a/src/bindgen/ir/typedef.rs b/src/bindgen/ir/typedef.rs index e775a4e80..580cea33f 100644 --- a/src/bindgen/ir/typedef.rs +++ b/src/bindgen/ir/typedef.rs @@ -11,7 +11,7 @@ use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ AnnotationSet, Cfg, Documentation, Field, GenericArgument, GenericParams, Item, ItemContainer, - Path, Struct, Type, + Path, Struct, TransparentTypeEraser, Type, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; @@ -30,6 +30,9 @@ pub struct Typedef { } impl Typedef { + // Name of the annotation that identifies a transparent typedef. + pub const TRANSPARENT_TYPEDEF: &'static str = "transparent-typedef"; + pub fn load(item: &syn::ItemType, mod_cfg: Option<&Cfg>) -> Result { if let Some(x) = Type::load(&item.ty)? { let path = Path::new(item.ident.unraw().to_string()); @@ -66,10 +69,6 @@ impl Typedef { } } - pub fn simplify_standard_types(&mut self, config: &Config) { - self.aliased.simplify_standard_types(config); - } - // Used to convert a transparent Struct to a Typedef. pub fn new_from_struct_field(item: &Struct, field: &Field) -> Self { Self { @@ -102,6 +101,21 @@ impl Typedef { } } + /// Returns the aliased type if this typedef is transparent, else None. + pub fn as_transparent_alias(&self, generics: &[GenericArgument]) -> Option { + if self + .annotations + .bool(Self::TRANSPARENT_TYPEDEF) + .unwrap_or(false) + { + let generics = self.generic_params.defaulted_generics(generics); + let mappings = self.generic_params.call(self.name(), &generics); + Some(self.aliased.specialize(&mappings)) + } else { + None + } + } + pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) { // Generic structs can instantiate monomorphs only once they've been // instantiated. See `instantiate_monomorph` for more details. @@ -156,6 +170,17 @@ impl Item for Typedef { &self.generic_params } + fn erase_transparent_types_inplace( + &mut self, + library: &Library, + eraser: &mut TransparentTypeEraser, + generics: &[GenericArgument], + ) { + let generics = self.generic_params.defaulted_generics(generics); + let mappings = self.generic_params.call(self.name(), &generics); + eraser.erase_transparent_types_inplace(library, &mut self.aliased, &mappings); + } + fn rename_for_config(&mut self, config: &Config) { config.export.rename(&mut self.export_name); self.aliased.rename_for_config(config, &self.generic_params); diff --git a/src/bindgen/ir/union.rs b/src/bindgen/ir/union.rs index 410e21a27..982b3168f 100644 --- a/src/bindgen/ir/union.rs +++ b/src/bindgen/ir/union.rs @@ -9,7 +9,7 @@ use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::ir::{ AnnotationSet, Cfg, Documentation, Field, GenericArgument, GenericParams, Item, ItemContainer, - Path, Repr, ReprAlign, ReprStyle, + Path, Repr, ReprAlign, ReprStyle, TransparentTypeEraser, }; use crate::bindgen::library::Library; use crate::bindgen::mangle; @@ -94,12 +94,6 @@ impl Union { } } - pub fn simplify_standard_types(&mut self, config: &Config) { - for field in &mut self.fields { - field.ty.simplify_standard_types(config); - } - } - pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) { // Generic unions can instantiate monomorphs only once they've been // instantiated. See `instantiate_monomorph` for more details. @@ -162,6 +156,19 @@ impl Item for Union { &self.generic_params } + fn erase_transparent_types_inplace( + &mut self, + library: &Library, + eraser: &mut TransparentTypeEraser, + generics: &[GenericArgument], + ) { + let generics = self.generic_params.defaulted_generics(generics); + let mappings = self.generic_params.call(self.name(), &generics); + for field in &mut self.fields { + eraser.erase_transparent_types_inplace(library, &mut field.ty, &mappings); + } + } + fn rename_for_config(&mut self, config: &Config) { config.export.rename(&mut self.export_name); for field in &mut self.fields { diff --git a/src/bindgen/library.rs b/src/bindgen/library.rs index 9d61257f9..375dc7e70 100644 --- a/src/bindgen/library.rs +++ b/src/bindgen/library.rs @@ -10,7 +10,9 @@ use crate::bindgen::config::{Config, Language, SortKey}; use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver; use crate::bindgen::dependencies::Dependencies; use crate::bindgen::error::Error; -use crate::bindgen::ir::{Constant, Enum, Function, Item, ItemContainer, ItemMap}; +use crate::bindgen::ir::{ + Constant, Enum, Function, Item, ItemContainer, ItemMap, TransparentTypeEraser, +}; use crate::bindgen::ir::{OpaqueItem, Path, Static, Struct, Typedef, Union}; use crate::bindgen::monomorph::Monomorphs; use crate::bindgen::ItemType; @@ -61,8 +63,9 @@ impl Library { } pub fn generate(mut self) -> Result { + let mut eraser = TransparentTypeEraser::default(); + self.erase_transparent_types(&mut eraser); self.transfer_annotations(); - self.simplify_standard_types(); match self.config.function.sort_by.unwrap_or(self.config.sort_by) { SortKey::Name => self.functions.sort_by(|x, y| x.path.cmp(&y.path)), @@ -70,7 +73,7 @@ impl Library { } if self.config.language != Language::Cxx { - self.instantiate_monomorphs(); + self.instantiate_monomorphs(&mut eraser); } self.remove_excluded(); if self.config.language == Language::C { @@ -172,6 +175,39 @@ impl Library { &self.config } + fn erase_transparent_types_for_items( + &self, + eraser: &mut TransparentTypeEraser, + items: &ItemMap, + ) -> ItemMap { + // NOTE: Because `items` is actually a shared reference to `self`, we cannot take it as + // mutable. We also cannot `drain` or `take` it first, because then the items would be + // unavailable for lookup during the type erasure process. So we mutate a clone, and let the + // caller assign the result back after these shared references have died. + let mut items = items.clone(); + items.for_all_items_mut(|item| { + item.erase_transparent_types_inplace(self, eraser, &[]); + }); + items + } + + fn erase_transparent_types(&mut self, eraser: &mut TransparentTypeEraser) { + self.constants = self.erase_transparent_types_for_items(eraser, &self.constants); + self.globals = self.erase_transparent_types_for_items(eraser, &self.globals); + self.enums = self.erase_transparent_types_for_items(eraser, &self.enums); + self.structs = self.erase_transparent_types_for_items(eraser, &self.structs); + self.unions = self.erase_transparent_types_for_items(eraser, &self.unions); + self.opaque_items = self.erase_transparent_types_for_items(eraser, &self.opaque_items); + self.typedefs = self.erase_transparent_types_for_items(eraser, &self.typedefs); + + // Functions do not `impl Item` and are not stored in an `ItemMap`, so do them manually. + let mut functions = self.functions.clone(); + for f in &mut functions { + f.erase_transparent_types_inplace(self, eraser); + } + self.functions = functions; + } + fn remove_excluded(&mut self) { let config = &self.config; // FIXME: interpret `config.export.exclude` as `Path`s. @@ -367,30 +403,7 @@ impl Library { } } - fn simplify_standard_types(&mut self) { - let config = &self.config; - - self.structs.for_all_items_mut(|x| { - x.simplify_standard_types(config); - }); - self.enums.for_all_items_mut(|x| { - x.simplify_standard_types(config); - }); - self.unions.for_all_items_mut(|x| { - x.simplify_standard_types(config); - }); - self.globals.for_all_items_mut(|x| { - x.simplify_standard_types(config); - }); - self.typedefs.for_all_items_mut(|x| { - x.simplify_standard_types(config); - }); - for x in &mut self.functions { - x.simplify_standard_types(config); - } - } - - fn instantiate_monomorphs(&mut self) { + fn instantiate_monomorphs(&mut self, eraser: &mut TransparentTypeEraser) { // Collect a list of monomorphs let mut monomorphs = Monomorphs::default(); @@ -411,19 +424,24 @@ impl Library { } // Insert the monomorphs into self - for monomorph in monomorphs.drain_structs() { + for mut monomorph in monomorphs.drain_structs() { + monomorph.erase_transparent_types_inplace(self, eraser, &[]); self.structs.try_insert(monomorph); } - for monomorph in monomorphs.drain_unions() { + for mut monomorph in monomorphs.drain_unions() { + monomorph.erase_transparent_types_inplace(self, eraser, &[]); self.unions.try_insert(monomorph); } - for monomorph in monomorphs.drain_opaques() { + for mut monomorph in monomorphs.drain_opaques() { + monomorph.erase_transparent_types_inplace(self, eraser, &[]); self.opaque_items.try_insert(monomorph); } - for monomorph in monomorphs.drain_typedefs() { + for mut monomorph in monomorphs.drain_typedefs() { + monomorph.erase_transparent_types_inplace(self, eraser, &[]); self.typedefs.try_insert(monomorph); } - for monomorph in monomorphs.drain_enums() { + for mut monomorph in monomorphs.drain_enums() { + monomorph.erase_transparent_types_inplace(self, eraser, &[]); self.enums.try_insert(monomorph); } diff --git a/src/bindgen/parser.rs b/src/bindgen/parser.rs index eb2ef2dc0..edd1f35d0 100644 --- a/src/bindgen/parser.rs +++ b/src/bindgen/parser.rs @@ -12,11 +12,12 @@ use syn::ext::IdentExt; use crate::bindgen::bitflags; use crate::bindgen::cargo::{Cargo, PackageRef}; -use crate::bindgen::config::{Config, ParseConfig}; +use crate::bindgen::config::{Config, Language, ParseConfig}; use crate::bindgen::error::Error; use crate::bindgen::ir::{ - AnnotationSet, AnnotationValue, Cfg, Constant, Documentation, Enum, Function, GenericParam, - GenericParams, ItemMap, OpaqueItem, Path, Static, Struct, Type, Typedef, Union, + AnnotationSet, AnnotationValue, Cfg, Constant, Documentation, Enum, Function, GenericArgument, + GenericParam, GenericParams, GenericPath, ItemMap, OpaqueItem, Path, Static, Struct, Type, + Typedef, Union, }; use crate::bindgen::utilities::{SynAbiHelpers, SynAttributeHelpers, SynItemHelpers}; @@ -438,39 +439,76 @@ impl Parse { } } - pub fn add_std_types(&mut self) { - let mut add_opaque = |path: &str, generic_params: Vec<&str>| { - let path = Path::new(path); - let generic_params: Vec<_> = generic_params - .into_iter() - .map(GenericParam::new_type_param) - .collect(); - self.opaque_items.try_insert(OpaqueItem::new( - path, - GenericParams(generic_params), - None, - AnnotationSet::new(), - Documentation::none(), - )) - }; + fn create_generic_params(param_names: &[&str]) -> Vec { + param_names + .iter() + .map(|name| GenericParam::new_type_param(name)) + .collect() + } + + fn add_opaque(&mut self, path: &str, generic_param_names: &[&str]) { + let path = Path::new(path); + self.opaque_items.try_insert(OpaqueItem::new( + path, + GenericParams(Self::create_generic_params(generic_param_names)), + None, + AnnotationSet::new(), + Documentation::none(), + )); + } + + fn add_transparent_typedef(&mut self, name: &str, aliased: Type, generics: &[&str]) { + let mut annotations = AnnotationSet::new(); + annotations.add_default(Typedef::TRANSPARENT_TYPEDEF, AnnotationValue::Bool(true)); + self.typedefs.try_insert(Typedef::new( + Path::new(name), + GenericParams(Self::create_generic_params(generics)), + aliased, + None, + annotations, + Documentation::none(), + )); + } + + pub fn add_std_types(&mut self, language: Language) { + // Well-known always-opaque types + self.add_opaque("Arc", &["T"]); + self.add_opaque("BTreeMap", &["K", "V"]); + self.add_opaque("BTreeSet", &["T"]); + self.add_opaque("HashMap", &["K", "V", "Hasher"]); + self.add_opaque("HashSet", &["T"]); + self.add_opaque("LinkedList", &["T"]); + self.add_opaque("Rc", &["T"]); + self.add_opaque("RefCell", &["T"]); + self.add_opaque("Result", &["T", "E"]); + self.add_opaque("String", &[]); + self.add_opaque("Vec", &["T"]); + self.add_opaque("VecDeque", &["T"]); + + // Well-known types with special erasure semantics (see [Type::erase_transparent_types]). + self.add_opaque("NonNull", &["T"]); + self.add_opaque("NonZero", &["T"]); + self.add_opaque("Option", &["T"]); + + // Well-known types whose treatment depends on the output language + let tpath = Type::Path(GenericPath::new(Path::new("T"), vec![])); + if language == Language::Cxx { + self.add_opaque("Box", &["T"]); + self.add_opaque("ManuallyDrop", &["T"]); + self.add_opaque("MaybeUninit", &["T"]); + self.add_opaque("Pin", &["T"]); + } else { + // Box acts like NonNull in C + let generics = vec![GenericArgument::Type(tpath.clone())]; + let alias = Type::Path(GenericPath::new(Path::new("NonNull"), generics)); + self.add_transparent_typedef("Box", alias, &["T"]); + self.add_transparent_typedef("ManuallyDrop", tpath.clone(), &["T"]); + self.add_transparent_typedef("MaybeUninit", tpath.clone(), &["T"]); + self.add_transparent_typedef("Pin", tpath.clone(), &["T"]); + } - add_opaque("String", vec![]); - add_opaque("Box", vec!["T"]); - add_opaque("RefCell", vec!["T"]); - add_opaque("Rc", vec!["T"]); - add_opaque("Arc", vec!["T"]); - add_opaque("Result", vec!["T", "E"]); - add_opaque("Option", vec!["T"]); - add_opaque("NonNull", vec!["T"]); - add_opaque("Vec", vec!["T"]); - add_opaque("HashMap", vec!["K", "V", "Hasher"]); - add_opaque("BTreeMap", vec!["K", "V"]); - add_opaque("HashSet", vec!["T"]); - add_opaque("BTreeSet", vec!["T"]); - add_opaque("LinkedList", vec!["T"]); - add_opaque("VecDeque", vec!["T"]); - add_opaque("ManuallyDrop", vec!["T"]); - add_opaque("MaybeUninit", vec!["T"]); + // Well-known and always-erased types + self.add_transparent_typedef("Cell", tpath.clone(), &["T"]); } pub fn extend_with(&mut self, other: &Parse) { diff --git a/tests/expectations/const_transparent.cpp b/tests/expectations/const_transparent.cpp index 963ee7fed..285fd8eaf 100644 --- a/tests/expectations/const_transparent.cpp +++ b/tests/expectations/const_transparent.cpp @@ -15,12 +15,12 @@ constexpr static const Wrapper TransparentStruct_ASSOC_STRUCT using TransparentTupleStruct = uint8_t; template -using TransparentStructWithErasedField = Wrapper; +using TransparentStructWithErasedField = T; constexpr static const TransparentStruct STRUCT_FOO = 4; constexpr static const TransparentTupleStruct STRUCT_BAR = 5; -constexpr static const Wrapper STRUCT_BAZ = 6; +constexpr static const TransparentStruct STRUCT_BAZ = 6; constexpr static const TransparentStructWithErasedField COMPLEX = 7; diff --git a/tests/expectations/transparent.c b/tests/expectations/transparent.c index 3354bc294..80569f155 100644 --- a/tests/expectations/transparent.c +++ b/tests/expectations/transparent.c @@ -7,6 +7,8 @@ typedef struct DummyStruct DummyStruct; typedef struct EnumWithAssociatedConstantInImpl EnumWithAssociatedConstantInImpl; +typedef struct StructWithAssociatedConstantInImpl StructWithAssociatedConstantInImpl; + typedef DummyStruct TransparentComplexWrappingStructTuple; typedef uint32_t TransparentPrimitiveWrappingStructTuple; @@ -27,7 +29,21 @@ typedef struct { } TransparentEmptyStructure; -#define EnumWithAssociatedConstantInImpl_TEN 10 +typedef const uint32_t *TransparentPointerWrappingStructure; + +typedef int32_t TransparentIntStruct; + +typedef DummyStruct TransparentComplexStruct; + +typedef TransparentPrimitiveWrappingStructure TransparentTransparentStruct; + +typedef uint32_t *TransparentNonNullStruct; + +typedef uint32_t *TransparentOptionNonNullStruct; + +#define StructWithAssociatedConstantInImpl_STRUCT_TEN 10 + +#define EnumWithAssociatedConstantInImpl_ENUM_TEN 10 void root(TransparentComplexWrappingStructTuple a, TransparentPrimitiveWrappingStructTuple b, @@ -37,4 +53,21 @@ void root(TransparentComplexWrappingStructTuple a, TransparentPrimitiveWrapper_i32 f, TransparentPrimitiveWithAssociatedConstants g, TransparentEmptyStructure h, - EnumWithAssociatedConstantInImpl i); + TransparentPointerWrappingStructure i, + StructWithAssociatedConstantInImpl j, + EnumWithAssociatedConstantInImpl k); + +void erased_root(uint32_t *a, + uint32_t *b, + TransparentPrimitiveWrappingStructure c, + uint32_t *d, + TransparentIntStruct e, + int32_t f, + DummyStruct g, + uint32_t *h, + int32_t i, + TransparentIntStruct j, + TransparentComplexStruct k, + TransparentTransparentStruct l, + TransparentNonNullStruct m, + TransparentOptionNonNullStruct n); diff --git a/tests/expectations/transparent.compat.c b/tests/expectations/transparent.compat.c index 1ebe9a037..2cf10ed9e 100644 --- a/tests/expectations/transparent.compat.c +++ b/tests/expectations/transparent.compat.c @@ -7,6 +7,8 @@ typedef struct DummyStruct DummyStruct; typedef struct EnumWithAssociatedConstantInImpl EnumWithAssociatedConstantInImpl; +typedef struct StructWithAssociatedConstantInImpl StructWithAssociatedConstantInImpl; + typedef DummyStruct TransparentComplexWrappingStructTuple; typedef uint32_t TransparentPrimitiveWrappingStructTuple; @@ -27,7 +29,21 @@ typedef struct { } TransparentEmptyStructure; -#define EnumWithAssociatedConstantInImpl_TEN 10 +typedef const uint32_t *TransparentPointerWrappingStructure; + +typedef int32_t TransparentIntStruct; + +typedef DummyStruct TransparentComplexStruct; + +typedef TransparentPrimitiveWrappingStructure TransparentTransparentStruct; + +typedef uint32_t *TransparentNonNullStruct; + +typedef uint32_t *TransparentOptionNonNullStruct; + +#define StructWithAssociatedConstantInImpl_STRUCT_TEN 10 + +#define EnumWithAssociatedConstantInImpl_ENUM_TEN 10 #ifdef __cplusplus extern "C" { @@ -41,7 +57,24 @@ void root(TransparentComplexWrappingStructTuple a, TransparentPrimitiveWrapper_i32 f, TransparentPrimitiveWithAssociatedConstants g, TransparentEmptyStructure h, - EnumWithAssociatedConstantInImpl i); + TransparentPointerWrappingStructure i, + StructWithAssociatedConstantInImpl j, + EnumWithAssociatedConstantInImpl k); + +void erased_root(uint32_t *a, + uint32_t *b, + TransparentPrimitiveWrappingStructure c, + uint32_t *d, + TransparentIntStruct e, + int32_t f, + DummyStruct g, + uint32_t *h, + int32_t i, + TransparentIntStruct j, + TransparentComplexStruct k, + TransparentTransparentStruct l, + TransparentNonNullStruct m, + TransparentOptionNonNullStruct n); #ifdef __cplusplus } // extern "C" diff --git a/tests/expectations/transparent.cpp b/tests/expectations/transparent.cpp index aa6091ca6..4459f2463 100644 --- a/tests/expectations/transparent.cpp +++ b/tests/expectations/transparent.cpp @@ -8,6 +8,8 @@ struct DummyStruct; struct EnumWithAssociatedConstantInImpl; +struct StructWithAssociatedConstantInImpl; + using TransparentComplexWrappingStructTuple = DummyStruct; using TransparentPrimitiveWrappingStructTuple = uint32_t; @@ -30,7 +32,21 @@ struct TransparentEmptyStructure { }; -constexpr static const TransparentPrimitiveWrappingStructure EnumWithAssociatedConstantInImpl_TEN = 10; +using TransparentPointerWrappingStructure = const uint32_t*; + +using TransparentIntStruct = int32_t; + +using TransparentComplexStruct = DummyStruct; + +using TransparentTransparentStruct = TransparentPrimitiveWrappingStructure; + +using TransparentNonNullStruct = uint32_t*; + +using TransparentOptionNonNullStruct = uint32_t*; + +constexpr static const TransparentPrimitiveWrappingStructure StructWithAssociatedConstantInImpl_STRUCT_TEN = 10; + +constexpr static const TransparentPrimitiveWrappingStructure EnumWithAssociatedConstantInImpl_ENUM_TEN = 10; extern "C" { @@ -42,6 +58,23 @@ void root(TransparentComplexWrappingStructTuple a, TransparentPrimitiveWrapper f, TransparentPrimitiveWithAssociatedConstants g, TransparentEmptyStructure h, - EnumWithAssociatedConstantInImpl i); + TransparentPointerWrappingStructure i, + StructWithAssociatedConstantInImpl j, + EnumWithAssociatedConstantInImpl k); + +void erased_root(uint32_t *a, + uint32_t *b, + TransparentPrimitiveWrappingStructure c, + uint32_t *d, + TransparentIntStruct e, + int32_t f, + DummyStruct g, + uint32_t *h, + int32_t i, + TransparentIntStruct j, + TransparentComplexStruct k, + TransparentTransparentStruct l, + TransparentNonNullStruct m, + TransparentOptionNonNullStruct n); } // extern "C" diff --git a/tests/expectations/transparent.pyx b/tests/expectations/transparent.pyx index 839c77427..e300f99ae 100644 --- a/tests/expectations/transparent.pyx +++ b/tests/expectations/transparent.pyx @@ -12,6 +12,9 @@ cdef extern from *: ctypedef struct EnumWithAssociatedConstantInImpl: pass + ctypedef struct StructWithAssociatedConstantInImpl: + pass + ctypedef DummyStruct TransparentComplexWrappingStructTuple; ctypedef uint32_t TransparentPrimitiveWrappingStructTuple; @@ -31,7 +34,21 @@ cdef extern from *: ctypedef struct TransparentEmptyStructure: pass - const TransparentPrimitiveWrappingStructure EnumWithAssociatedConstantInImpl_TEN # = 10 + ctypedef const uint32_t *TransparentPointerWrappingStructure; + + ctypedef int32_t TransparentIntStruct; + + ctypedef DummyStruct TransparentComplexStruct; + + ctypedef TransparentPrimitiveWrappingStructure TransparentTransparentStruct; + + ctypedef uint32_t *TransparentNonNullStruct; + + ctypedef uint32_t *TransparentOptionNonNullStruct; + + const TransparentPrimitiveWrappingStructure StructWithAssociatedConstantInImpl_STRUCT_TEN # = 10 + + const TransparentPrimitiveWrappingStructure EnumWithAssociatedConstantInImpl_ENUM_TEN # = 10 void root(TransparentComplexWrappingStructTuple a, TransparentPrimitiveWrappingStructTuple b, @@ -41,4 +58,21 @@ cdef extern from *: TransparentPrimitiveWrapper_i32 f, TransparentPrimitiveWithAssociatedConstants g, TransparentEmptyStructure h, - EnumWithAssociatedConstantInImpl i); + TransparentPointerWrappingStructure i, + StructWithAssociatedConstantInImpl j, + EnumWithAssociatedConstantInImpl k); + + void erased_root(uint32_t *a, + uint32_t *b, + TransparentPrimitiveWrappingStructure c, + uint32_t *d, + TransparentIntStruct e, + int32_t f, + DummyStruct g, + uint32_t *h, + int32_t i, + TransparentIntStruct j, + TransparentComplexStruct k, + TransparentTransparentStruct l, + TransparentNonNullStruct m, + TransparentOptionNonNullStruct n); diff --git a/tests/expectations/transparent_both.c b/tests/expectations/transparent_both.c index df9b8a57d..042009d51 100644 --- a/tests/expectations/transparent_both.c +++ b/tests/expectations/transparent_both.c @@ -7,6 +7,8 @@ typedef struct DummyStruct DummyStruct; typedef struct EnumWithAssociatedConstantInImpl EnumWithAssociatedConstantInImpl; +typedef struct StructWithAssociatedConstantInImpl StructWithAssociatedConstantInImpl; + typedef struct DummyStruct TransparentComplexWrappingStructTuple; typedef uint32_t TransparentPrimitiveWrappingStructTuple; @@ -27,7 +29,21 @@ typedef struct TransparentEmptyStructure { } TransparentEmptyStructure; -#define EnumWithAssociatedConstantInImpl_TEN 10 +typedef const uint32_t *TransparentPointerWrappingStructure; + +typedef int32_t TransparentIntStruct; + +typedef struct DummyStruct TransparentComplexStruct; + +typedef TransparentPrimitiveWrappingStructure TransparentTransparentStruct; + +typedef uint32_t *TransparentNonNullStruct; + +typedef uint32_t *TransparentOptionNonNullStruct; + +#define StructWithAssociatedConstantInImpl_STRUCT_TEN 10 + +#define EnumWithAssociatedConstantInImpl_ENUM_TEN 10 void root(TransparentComplexWrappingStructTuple a, TransparentPrimitiveWrappingStructTuple b, @@ -37,4 +53,21 @@ void root(TransparentComplexWrappingStructTuple a, TransparentPrimitiveWrapper_i32 f, TransparentPrimitiveWithAssociatedConstants g, struct TransparentEmptyStructure h, - struct EnumWithAssociatedConstantInImpl i); + TransparentPointerWrappingStructure i, + struct StructWithAssociatedConstantInImpl j, + struct EnumWithAssociatedConstantInImpl k); + +void erased_root(uint32_t *a, + uint32_t *b, + TransparentPrimitiveWrappingStructure c, + uint32_t *d, + TransparentIntStruct e, + int32_t f, + struct DummyStruct g, + uint32_t *h, + int32_t i, + TransparentIntStruct j, + TransparentComplexStruct k, + TransparentTransparentStruct l, + TransparentNonNullStruct m, + TransparentOptionNonNullStruct n); diff --git a/tests/expectations/transparent_both.compat.c b/tests/expectations/transparent_both.compat.c index 4acd6677d..21872104d 100644 --- a/tests/expectations/transparent_both.compat.c +++ b/tests/expectations/transparent_both.compat.c @@ -7,6 +7,8 @@ typedef struct DummyStruct DummyStruct; typedef struct EnumWithAssociatedConstantInImpl EnumWithAssociatedConstantInImpl; +typedef struct StructWithAssociatedConstantInImpl StructWithAssociatedConstantInImpl; + typedef struct DummyStruct TransparentComplexWrappingStructTuple; typedef uint32_t TransparentPrimitiveWrappingStructTuple; @@ -27,7 +29,21 @@ typedef struct TransparentEmptyStructure { } TransparentEmptyStructure; -#define EnumWithAssociatedConstantInImpl_TEN 10 +typedef const uint32_t *TransparentPointerWrappingStructure; + +typedef int32_t TransparentIntStruct; + +typedef struct DummyStruct TransparentComplexStruct; + +typedef TransparentPrimitiveWrappingStructure TransparentTransparentStruct; + +typedef uint32_t *TransparentNonNullStruct; + +typedef uint32_t *TransparentOptionNonNullStruct; + +#define StructWithAssociatedConstantInImpl_STRUCT_TEN 10 + +#define EnumWithAssociatedConstantInImpl_ENUM_TEN 10 #ifdef __cplusplus extern "C" { @@ -41,7 +57,24 @@ void root(TransparentComplexWrappingStructTuple a, TransparentPrimitiveWrapper_i32 f, TransparentPrimitiveWithAssociatedConstants g, struct TransparentEmptyStructure h, - struct EnumWithAssociatedConstantInImpl i); + TransparentPointerWrappingStructure i, + struct StructWithAssociatedConstantInImpl j, + struct EnumWithAssociatedConstantInImpl k); + +void erased_root(uint32_t *a, + uint32_t *b, + TransparentPrimitiveWrappingStructure c, + uint32_t *d, + TransparentIntStruct e, + int32_t f, + struct DummyStruct g, + uint32_t *h, + int32_t i, + TransparentIntStruct j, + TransparentComplexStruct k, + TransparentTransparentStruct l, + TransparentNonNullStruct m, + TransparentOptionNonNullStruct n); #ifdef __cplusplus } // extern "C" diff --git a/tests/expectations/transparent_tag.c b/tests/expectations/transparent_tag.c index 2f6dea68e..6589eb6bc 100644 --- a/tests/expectations/transparent_tag.c +++ b/tests/expectations/transparent_tag.c @@ -7,6 +7,8 @@ struct DummyStruct; struct EnumWithAssociatedConstantInImpl; +struct StructWithAssociatedConstantInImpl; + typedef struct DummyStruct TransparentComplexWrappingStructTuple; typedef uint32_t TransparentPrimitiveWrappingStructTuple; @@ -27,7 +29,21 @@ struct TransparentEmptyStructure { }; -#define EnumWithAssociatedConstantInImpl_TEN 10 +typedef const uint32_t *TransparentPointerWrappingStructure; + +typedef int32_t TransparentIntStruct; + +typedef struct DummyStruct TransparentComplexStruct; + +typedef TransparentPrimitiveWrappingStructure TransparentTransparentStruct; + +typedef uint32_t *TransparentNonNullStruct; + +typedef uint32_t *TransparentOptionNonNullStruct; + +#define StructWithAssociatedConstantInImpl_STRUCT_TEN 10 + +#define EnumWithAssociatedConstantInImpl_ENUM_TEN 10 void root(TransparentComplexWrappingStructTuple a, TransparentPrimitiveWrappingStructTuple b, @@ -37,4 +53,21 @@ void root(TransparentComplexWrappingStructTuple a, TransparentPrimitiveWrapper_i32 f, TransparentPrimitiveWithAssociatedConstants g, struct TransparentEmptyStructure h, - struct EnumWithAssociatedConstantInImpl i); + TransparentPointerWrappingStructure i, + struct StructWithAssociatedConstantInImpl j, + struct EnumWithAssociatedConstantInImpl k); + +void erased_root(uint32_t *a, + uint32_t *b, + TransparentPrimitiveWrappingStructure c, + uint32_t *d, + TransparentIntStruct e, + int32_t f, + struct DummyStruct g, + uint32_t *h, + int32_t i, + TransparentIntStruct j, + TransparentComplexStruct k, + TransparentTransparentStruct l, + TransparentNonNullStruct m, + TransparentOptionNonNullStruct n); diff --git a/tests/expectations/transparent_tag.compat.c b/tests/expectations/transparent_tag.compat.c index 508d6ad67..2eff06ef0 100644 --- a/tests/expectations/transparent_tag.compat.c +++ b/tests/expectations/transparent_tag.compat.c @@ -7,6 +7,8 @@ struct DummyStruct; struct EnumWithAssociatedConstantInImpl; +struct StructWithAssociatedConstantInImpl; + typedef struct DummyStruct TransparentComplexWrappingStructTuple; typedef uint32_t TransparentPrimitiveWrappingStructTuple; @@ -27,7 +29,21 @@ struct TransparentEmptyStructure { }; -#define EnumWithAssociatedConstantInImpl_TEN 10 +typedef const uint32_t *TransparentPointerWrappingStructure; + +typedef int32_t TransparentIntStruct; + +typedef struct DummyStruct TransparentComplexStruct; + +typedef TransparentPrimitiveWrappingStructure TransparentTransparentStruct; + +typedef uint32_t *TransparentNonNullStruct; + +typedef uint32_t *TransparentOptionNonNullStruct; + +#define StructWithAssociatedConstantInImpl_STRUCT_TEN 10 + +#define EnumWithAssociatedConstantInImpl_ENUM_TEN 10 #ifdef __cplusplus extern "C" { @@ -41,7 +57,24 @@ void root(TransparentComplexWrappingStructTuple a, TransparentPrimitiveWrapper_i32 f, TransparentPrimitiveWithAssociatedConstants g, struct TransparentEmptyStructure h, - struct EnumWithAssociatedConstantInImpl i); + TransparentPointerWrappingStructure i, + struct StructWithAssociatedConstantInImpl j, + struct EnumWithAssociatedConstantInImpl k); + +void erased_root(uint32_t *a, + uint32_t *b, + TransparentPrimitiveWrappingStructure c, + uint32_t *d, + TransparentIntStruct e, + int32_t f, + struct DummyStruct g, + uint32_t *h, + int32_t i, + TransparentIntStruct j, + TransparentComplexStruct k, + TransparentTransparentStruct l, + TransparentNonNullStruct m, + TransparentOptionNonNullStruct n); #ifdef __cplusplus } // extern "C" diff --git a/tests/expectations/transparent_tag.pyx b/tests/expectations/transparent_tag.pyx index 15d66f74c..204452d64 100644 --- a/tests/expectations/transparent_tag.pyx +++ b/tests/expectations/transparent_tag.pyx @@ -12,6 +12,9 @@ cdef extern from *: cdef struct EnumWithAssociatedConstantInImpl: pass + cdef struct StructWithAssociatedConstantInImpl: + pass + ctypedef DummyStruct TransparentComplexWrappingStructTuple; ctypedef uint32_t TransparentPrimitiveWrappingStructTuple; @@ -31,7 +34,21 @@ cdef extern from *: cdef struct TransparentEmptyStructure: pass - const TransparentPrimitiveWrappingStructure EnumWithAssociatedConstantInImpl_TEN # = 10 + ctypedef const uint32_t *TransparentPointerWrappingStructure; + + ctypedef int32_t TransparentIntStruct; + + ctypedef DummyStruct TransparentComplexStruct; + + ctypedef TransparentPrimitiveWrappingStructure TransparentTransparentStruct; + + ctypedef uint32_t *TransparentNonNullStruct; + + ctypedef uint32_t *TransparentOptionNonNullStruct; + + const TransparentPrimitiveWrappingStructure StructWithAssociatedConstantInImpl_STRUCT_TEN # = 10 + + const TransparentPrimitiveWrappingStructure EnumWithAssociatedConstantInImpl_ENUM_TEN # = 10 void root(TransparentComplexWrappingStructTuple a, TransparentPrimitiveWrappingStructTuple b, @@ -41,4 +58,21 @@ cdef extern from *: TransparentPrimitiveWrapper_i32 f, TransparentPrimitiveWithAssociatedConstants g, TransparentEmptyStructure h, - EnumWithAssociatedConstantInImpl i); + TransparentPointerWrappingStructure i, + StructWithAssociatedConstantInImpl j, + EnumWithAssociatedConstantInImpl k); + + void erased_root(uint32_t *a, + uint32_t *b, + TransparentPrimitiveWrappingStructure c, + uint32_t *d, + TransparentIntStruct e, + int32_t f, + DummyStruct g, + uint32_t *h, + int32_t i, + TransparentIntStruct j, + TransparentComplexStruct k, + TransparentTransparentStruct l, + TransparentNonNullStruct m, + TransparentOptionNonNullStruct n); diff --git a/tests/expectations/transparent_typedef.c b/tests/expectations/transparent_typedef.c new file mode 100644 index 000000000..331d48da8 --- /dev/null +++ b/tests/expectations/transparent_typedef.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +typedef struct Option_Option_i32 Option_Option_i32; + +typedef struct Option_i64 Option_i64; + +typedef struct { + int32_t a; + int32_t *n; + int32_t t; +} AlwaysErased1_i32; + +typedef struct { + int16_t *const *o; +} SometimesErased1_____i16; + +typedef struct { + const int32_t *o; +} SometimesErased1_i32; + +typedef struct { + const Option_i64 *o; +} SometimesErased1_i64; + +typedef struct { + int32_t aa; + int32_t *an; + int32_t at; + int32_t *na; + int32_t **nn; + int32_t *nt; + int32_t *on; + int32_t ta; + int32_t *tn; + int32_t tt; +} AlwaysErased2_i32; + +typedef struct { + int16_t *const *ao; + int16_t **const *no; + int16_t *const *oa; + int16_t *const *ot; + int16_t *const *to; +} SometimesErased2_____i16; + +typedef struct { + const int32_t *ao; + int32_t *const *no; + const int32_t *oa; + const int32_t *ot; + const int32_t *to; +} SometimesErased2_i32; + +typedef struct { + const Option_i64 *ao; + Option_i64 *const *no; + const Option_i64 *oa; + const Option_i64 *ot; + const Option_i64 *to; +} SometimesErased2_i64; + +typedef struct { + const Option_Option_i32 *oo; +} NeverErased2_i32; + +typedef struct { + int32_t *tont; + int32_t *otnt; + int32_t *totn; + int32_t *totnt; +} AlwaysErasedMany_i32; + +void root1(AlwaysErased1_i32 a, + SometimesErased1_____i16 sn, + SometimesErased1_i32 sz, + SometimesErased1_i64 si); + +void root2(AlwaysErased2_i32 a, + SometimesErased2_____i16 sn, + SometimesErased2_i32 sz, + SometimesErased2_i64 si, + NeverErased2_i32 n); + +void root_many(AlwaysErasedMany_i32 a); diff --git a/tests/expectations/transparent_typedef.compat.c b/tests/expectations/transparent_typedef.compat.c new file mode 100644 index 000000000..0cbcbfd76 --- /dev/null +++ b/tests/expectations/transparent_typedef.compat.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include + +typedef struct Option_Option_i32 Option_Option_i32; + +typedef struct Option_i64 Option_i64; + +typedef struct { + int32_t a; + int32_t *n; + int32_t t; +} AlwaysErased1_i32; + +typedef struct { + int16_t *const *o; +} SometimesErased1_____i16; + +typedef struct { + const int32_t *o; +} SometimesErased1_i32; + +typedef struct { + const Option_i64 *o; +} SometimesErased1_i64; + +typedef struct { + int32_t aa; + int32_t *an; + int32_t at; + int32_t *na; + int32_t **nn; + int32_t *nt; + int32_t *on; + int32_t ta; + int32_t *tn; + int32_t tt; +} AlwaysErased2_i32; + +typedef struct { + int16_t *const *ao; + int16_t **const *no; + int16_t *const *oa; + int16_t *const *ot; + int16_t *const *to; +} SometimesErased2_____i16; + +typedef struct { + const int32_t *ao; + int32_t *const *no; + const int32_t *oa; + const int32_t *ot; + const int32_t *to; +} SometimesErased2_i32; + +typedef struct { + const Option_i64 *ao; + Option_i64 *const *no; + const Option_i64 *oa; + const Option_i64 *ot; + const Option_i64 *to; +} SometimesErased2_i64; + +typedef struct { + const Option_Option_i32 *oo; +} NeverErased2_i32; + +typedef struct { + int32_t *tont; + int32_t *otnt; + int32_t *totn; + int32_t *totnt; +} AlwaysErasedMany_i32; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root1(AlwaysErased1_i32 a, + SometimesErased1_____i16 sn, + SometimesErased1_i32 sz, + SometimesErased1_i64 si); + +void root2(AlwaysErased2_i32 a, + SometimesErased2_____i16 sn, + SometimesErased2_i32 sz, + SometimesErased2_i64 si, + NeverErased2_i32 n); + +void root_many(AlwaysErasedMany_i32 a); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/transparent_typedef.cpp b/tests/expectations/transparent_typedef.cpp new file mode 100644 index 000000000..17b2628d9 --- /dev/null +++ b/tests/expectations/transparent_typedef.cpp @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include + +template +struct Option; + +template +struct AlwaysErased1 { + T a; + T *n; + T t; +}; + +template +struct SometimesErased1 { + const Option *o; +}; + +template +struct AlwaysErased2 { + T aa; + T *an; + T at; + T *na; + T **nn; + T *nt; + T *on; + T ta; + T *tn; + T tt; +}; + +template +struct SometimesErased2 { + const Option *ao; + Option *const *no; + const Option *oa; + const Option *ot; + const Option *to; +}; + +template +struct NeverErased2 { + const Option> *oo; +}; + +template +struct AlwaysErasedMany { + T *tont; + T *otnt; + T *totn; + T *totnt; +}; + +extern "C" { + +void root1(AlwaysErased1 a, + SometimesErased1 sn, + SometimesErased1 sz, + SometimesErased1 si); + +void root2(AlwaysErased2 a, + SometimesErased2 sn, + SometimesErased2 sz, + SometimesErased2 si, + NeverErased2 n); + +void root_many(AlwaysErasedMany a); + +} // extern "C" diff --git a/tests/expectations/transparent_typedef.pyx b/tests/expectations/transparent_typedef.pyx new file mode 100644 index 000000000..ea11f12b6 --- /dev/null +++ b/tests/expectations/transparent_typedef.pyx @@ -0,0 +1,82 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + ctypedef struct Option_Option_i32: + pass + + ctypedef struct Option_i64: + pass + + ctypedef struct AlwaysErased1_i32: + int32_t a; + int32_t *n; + int32_t t; + + ctypedef struct SometimesErased1_____i16: + int16_t *const *o; + + ctypedef struct SometimesErased1_i32: + const int32_t *o; + + ctypedef struct SometimesErased1_i64: + const Option_i64 *o; + + ctypedef struct AlwaysErased2_i32: + int32_t aa; + int32_t *an; + int32_t at; + int32_t *na; + int32_t **nn; + int32_t *nt; + int32_t *on; + int32_t ta; + int32_t *tn; + int32_t tt; + + ctypedef struct SometimesErased2_____i16: + int16_t *const *ao; + int16_t **const *no; + int16_t *const *oa; + int16_t *const *ot; + int16_t *const *to; + + ctypedef struct SometimesErased2_i32: + const int32_t *ao; + int32_t *const *no; + const int32_t *oa; + const int32_t *ot; + const int32_t *to; + + ctypedef struct SometimesErased2_i64: + const Option_i64 *ao; + Option_i64 *const *no; + const Option_i64 *oa; + const Option_i64 *ot; + const Option_i64 *to; + + ctypedef struct NeverErased2_i32: + const Option_Option_i32 *oo; + + ctypedef struct AlwaysErasedMany_i32: + int32_t *tont; + int32_t *otnt; + int32_t *totn; + int32_t *totnt; + + void root1(AlwaysErased1_i32 a, + SometimesErased1_____i16 sn, + SometimesErased1_i32 sz, + SometimesErased1_i64 si); + + void root2(AlwaysErased2_i32 a, + SometimesErased2_____i16 sn, + SometimesErased2_i32 sz, + SometimesErased2_i64 si, + NeverErased2_i32 n); + + void root_many(AlwaysErasedMany_i32 a); diff --git a/tests/expectations/transparent_typedef_both.c b/tests/expectations/transparent_typedef_both.c new file mode 100644 index 000000000..40b693ba1 --- /dev/null +++ b/tests/expectations/transparent_typedef_both.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +typedef struct Option_Option_i32 Option_Option_i32; + +typedef struct Option_i64 Option_i64; + +typedef struct AlwaysErased1_i32 { + int32_t a; + int32_t *n; + int32_t t; +} AlwaysErased1_i32; + +typedef struct SometimesErased1_____i16 { + int16_t *const *o; +} SometimesErased1_____i16; + +typedef struct SometimesErased1_i32 { + const int32_t *o; +} SometimesErased1_i32; + +typedef struct SometimesErased1_i64 { + const struct Option_i64 *o; +} SometimesErased1_i64; + +typedef struct AlwaysErased2_i32 { + int32_t aa; + int32_t *an; + int32_t at; + int32_t *na; + int32_t **nn; + int32_t *nt; + int32_t *on; + int32_t ta; + int32_t *tn; + int32_t tt; +} AlwaysErased2_i32; + +typedef struct SometimesErased2_____i16 { + int16_t *const *ao; + int16_t **const *no; + int16_t *const *oa; + int16_t *const *ot; + int16_t *const *to; +} SometimesErased2_____i16; + +typedef struct SometimesErased2_i32 { + const int32_t *ao; + int32_t *const *no; + const int32_t *oa; + const int32_t *ot; + const int32_t *to; +} SometimesErased2_i32; + +typedef struct SometimesErased2_i64 { + const struct Option_i64 *ao; + struct Option_i64 *const *no; + const struct Option_i64 *oa; + const struct Option_i64 *ot; + const struct Option_i64 *to; +} SometimesErased2_i64; + +typedef struct NeverErased2_i32 { + const struct Option_Option_i32 *oo; +} NeverErased2_i32; + +typedef struct AlwaysErasedMany_i32 { + int32_t *tont; + int32_t *otnt; + int32_t *totn; + int32_t *totnt; +} AlwaysErasedMany_i32; + +void root1(struct AlwaysErased1_i32 a, + struct SometimesErased1_____i16 sn, + struct SometimesErased1_i32 sz, + struct SometimesErased1_i64 si); + +void root2(struct AlwaysErased2_i32 a, + struct SometimesErased2_____i16 sn, + struct SometimesErased2_i32 sz, + struct SometimesErased2_i64 si, + struct NeverErased2_i32 n); + +void root_many(struct AlwaysErasedMany_i32 a); diff --git a/tests/expectations/transparent_typedef_both.compat.c b/tests/expectations/transparent_typedef_both.compat.c new file mode 100644 index 000000000..7d09175c8 --- /dev/null +++ b/tests/expectations/transparent_typedef_both.compat.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include + +typedef struct Option_Option_i32 Option_Option_i32; + +typedef struct Option_i64 Option_i64; + +typedef struct AlwaysErased1_i32 { + int32_t a; + int32_t *n; + int32_t t; +} AlwaysErased1_i32; + +typedef struct SometimesErased1_____i16 { + int16_t *const *o; +} SometimesErased1_____i16; + +typedef struct SometimesErased1_i32 { + const int32_t *o; +} SometimesErased1_i32; + +typedef struct SometimesErased1_i64 { + const struct Option_i64 *o; +} SometimesErased1_i64; + +typedef struct AlwaysErased2_i32 { + int32_t aa; + int32_t *an; + int32_t at; + int32_t *na; + int32_t **nn; + int32_t *nt; + int32_t *on; + int32_t ta; + int32_t *tn; + int32_t tt; +} AlwaysErased2_i32; + +typedef struct SometimesErased2_____i16 { + int16_t *const *ao; + int16_t **const *no; + int16_t *const *oa; + int16_t *const *ot; + int16_t *const *to; +} SometimesErased2_____i16; + +typedef struct SometimesErased2_i32 { + const int32_t *ao; + int32_t *const *no; + const int32_t *oa; + const int32_t *ot; + const int32_t *to; +} SometimesErased2_i32; + +typedef struct SometimesErased2_i64 { + const struct Option_i64 *ao; + struct Option_i64 *const *no; + const struct Option_i64 *oa; + const struct Option_i64 *ot; + const struct Option_i64 *to; +} SometimesErased2_i64; + +typedef struct NeverErased2_i32 { + const struct Option_Option_i32 *oo; +} NeverErased2_i32; + +typedef struct AlwaysErasedMany_i32 { + int32_t *tont; + int32_t *otnt; + int32_t *totn; + int32_t *totnt; +} AlwaysErasedMany_i32; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root1(struct AlwaysErased1_i32 a, + struct SometimesErased1_____i16 sn, + struct SometimesErased1_i32 sz, + struct SometimesErased1_i64 si); + +void root2(struct AlwaysErased2_i32 a, + struct SometimesErased2_____i16 sn, + struct SometimesErased2_i32 sz, + struct SometimesErased2_i64 si, + struct NeverErased2_i32 n); + +void root_many(struct AlwaysErasedMany_i32 a); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/transparent_typedef_tag.c b/tests/expectations/transparent_typedef_tag.c new file mode 100644 index 000000000..92ba5bf88 --- /dev/null +++ b/tests/expectations/transparent_typedef_tag.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +struct Option_Option_i32; + +struct Option_i64; + +struct AlwaysErased1_i32 { + int32_t a; + int32_t *n; + int32_t t; +}; + +struct SometimesErased1_____i16 { + int16_t *const *o; +}; + +struct SometimesErased1_i32 { + const int32_t *o; +}; + +struct SometimesErased1_i64 { + const struct Option_i64 *o; +}; + +struct AlwaysErased2_i32 { + int32_t aa; + int32_t *an; + int32_t at; + int32_t *na; + int32_t **nn; + int32_t *nt; + int32_t *on; + int32_t ta; + int32_t *tn; + int32_t tt; +}; + +struct SometimesErased2_____i16 { + int16_t *const *ao; + int16_t **const *no; + int16_t *const *oa; + int16_t *const *ot; + int16_t *const *to; +}; + +struct SometimesErased2_i32 { + const int32_t *ao; + int32_t *const *no; + const int32_t *oa; + const int32_t *ot; + const int32_t *to; +}; + +struct SometimesErased2_i64 { + const struct Option_i64 *ao; + struct Option_i64 *const *no; + const struct Option_i64 *oa; + const struct Option_i64 *ot; + const struct Option_i64 *to; +}; + +struct NeverErased2_i32 { + const struct Option_Option_i32 *oo; +}; + +struct AlwaysErasedMany_i32 { + int32_t *tont; + int32_t *otnt; + int32_t *totn; + int32_t *totnt; +}; + +void root1(struct AlwaysErased1_i32 a, + struct SometimesErased1_____i16 sn, + struct SometimesErased1_i32 sz, + struct SometimesErased1_i64 si); + +void root2(struct AlwaysErased2_i32 a, + struct SometimesErased2_____i16 sn, + struct SometimesErased2_i32 sz, + struct SometimesErased2_i64 si, + struct NeverErased2_i32 n); + +void root_many(struct AlwaysErasedMany_i32 a); diff --git a/tests/expectations/transparent_typedef_tag.compat.c b/tests/expectations/transparent_typedef_tag.compat.c new file mode 100644 index 000000000..952651170 --- /dev/null +++ b/tests/expectations/transparent_typedef_tag.compat.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include + +struct Option_Option_i32; + +struct Option_i64; + +struct AlwaysErased1_i32 { + int32_t a; + int32_t *n; + int32_t t; +}; + +struct SometimesErased1_____i16 { + int16_t *const *o; +}; + +struct SometimesErased1_i32 { + const int32_t *o; +}; + +struct SometimesErased1_i64 { + const struct Option_i64 *o; +}; + +struct AlwaysErased2_i32 { + int32_t aa; + int32_t *an; + int32_t at; + int32_t *na; + int32_t **nn; + int32_t *nt; + int32_t *on; + int32_t ta; + int32_t *tn; + int32_t tt; +}; + +struct SometimesErased2_____i16 { + int16_t *const *ao; + int16_t **const *no; + int16_t *const *oa; + int16_t *const *ot; + int16_t *const *to; +}; + +struct SometimesErased2_i32 { + const int32_t *ao; + int32_t *const *no; + const int32_t *oa; + const int32_t *ot; + const int32_t *to; +}; + +struct SometimesErased2_i64 { + const struct Option_i64 *ao; + struct Option_i64 *const *no; + const struct Option_i64 *oa; + const struct Option_i64 *ot; + const struct Option_i64 *to; +}; + +struct NeverErased2_i32 { + const struct Option_Option_i32 *oo; +}; + +struct AlwaysErasedMany_i32 { + int32_t *tont; + int32_t *otnt; + int32_t *totn; + int32_t *totnt; +}; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void root1(struct AlwaysErased1_i32 a, + struct SometimesErased1_____i16 sn, + struct SometimesErased1_i32 sz, + struct SometimesErased1_i64 si); + +void root2(struct AlwaysErased2_i32 a, + struct SometimesErased2_____i16 sn, + struct SometimesErased2_i32 sz, + struct SometimesErased2_i64 si, + struct NeverErased2_i32 n); + +void root_many(struct AlwaysErasedMany_i32 a); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/transparent_typedef_tag.pyx b/tests/expectations/transparent_typedef_tag.pyx new file mode 100644 index 000000000..180322949 --- /dev/null +++ b/tests/expectations/transparent_typedef_tag.pyx @@ -0,0 +1,82 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + cdef struct Option_Option_i32: + pass + + cdef struct Option_i64: + pass + + cdef struct AlwaysErased1_i32: + int32_t a; + int32_t *n; + int32_t t; + + cdef struct SometimesErased1_____i16: + int16_t *const *o; + + cdef struct SometimesErased1_i32: + const int32_t *o; + + cdef struct SometimesErased1_i64: + const Option_i64 *o; + + cdef struct AlwaysErased2_i32: + int32_t aa; + int32_t *an; + int32_t at; + int32_t *na; + int32_t **nn; + int32_t *nt; + int32_t *on; + int32_t ta; + int32_t *tn; + int32_t tt; + + cdef struct SometimesErased2_____i16: + int16_t *const *ao; + int16_t **const *no; + int16_t *const *oa; + int16_t *const *ot; + int16_t *const *to; + + cdef struct SometimesErased2_i32: + const int32_t *ao; + int32_t *const *no; + const int32_t *oa; + const int32_t *ot; + const int32_t *to; + + cdef struct SometimesErased2_i64: + const Option_i64 *ao; + Option_i64 *const *no; + const Option_i64 *oa; + const Option_i64 *ot; + const Option_i64 *to; + + cdef struct NeverErased2_i32: + const Option_Option_i32 *oo; + + cdef struct AlwaysErasedMany_i32: + int32_t *tont; + int32_t *otnt; + int32_t *totn; + int32_t *totnt; + + void root1(AlwaysErased1_i32 a, + SometimesErased1_____i16 sn, + SometimesErased1_i32 sz, + SometimesErased1_i64 si); + + void root2(AlwaysErased2_i32 a, + SometimesErased2_____i16 sn, + SometimesErased2_i32 sz, + SometimesErased2_i64 si, + NeverErased2_i32 n); + + void root_many(AlwaysErasedMany_i32 a); diff --git a/tests/rust/const_transparent.rs b/tests/rust/const_transparent.rs index 83b9dacbe..1e2e81c9b 100644 --- a/tests/rust/const_transparent.rs +++ b/tests/rust/const_transparent.rs @@ -12,6 +12,7 @@ impl TransparentStruct { #[repr(transparent)] struct TransparentTupleStruct(u8); +/// cbindgen:transparent-typedef #[repr(transparent)] struct Wrapper { field: T } diff --git a/tests/rust/transparent.rs b/tests/rust/transparent.rs index e35200cf1..fb812e334 100644 --- a/tests/rust/transparent.rs +++ b/tests/rust/transparent.rs @@ -16,6 +16,10 @@ struct TransparentComplexWrappingStructure { only_field: DummyStruct } #[repr(transparent)] struct TransparentPrimitiveWrappingStructure { only_field: u32 } +// Transparent struct wrapping a pointer +#[repr(transparent)] +struct TransparentPointerWrappingStructure { only_field: *const u32 } + // Transparent struct wrapper with a marker wrapping a struct. #[repr(transparent)] struct TransparentComplexWrapper { @@ -53,10 +57,18 @@ impl TransparentPrimitiveWithAssociatedConstants { }; } +struct StructWithAssociatedConstantInImpl { } + +impl StructWithAssociatedConstantInImpl { + pub const STRUCT_TEN: TransparentPrimitiveWrappingStructure = + TransparentPrimitiveWrappingStructure { only_field: 10 }; +} + enum EnumWithAssociatedConstantInImpl { A } impl EnumWithAssociatedConstantInImpl { - pub const TEN: TransparentPrimitiveWrappingStructure = TransparentPrimitiveWrappingStructure { only_field: 10 }; + pub const ENUM_TEN: TransparentPrimitiveWrappingStructure = + TransparentPrimitiveWrappingStructure { only_field: 10 }; } #[no_mangle] @@ -69,5 +81,59 @@ pub extern "C" fn root( f: TransparentPrimitiveWrapper, g: TransparentPrimitiveWithAssociatedConstants, h: TransparentEmptyStructure, - i: EnumWithAssociatedConstantInImpl, + i: TransparentPointerWrappingStructure, + j: StructWithAssociatedConstantInImpl, + k: EnumWithAssociatedConstantInImpl, +) { } + +#[repr(transparent)] +/// cbindgen:transparent-typedef +struct ErasedTransparentNonNullPointerWrappingStruct { only_field: NonNull } + +#[repr(transparent)] +/// cbindgen:transparent-typedef +struct ErasedTransparentOptionalNonNullPointerWrappingStruct { only_field: Option> } + +#[repr(transparent)] +/// cbindgen:transparent-typedef +struct ErasedTransparentWrappingAnotherTransparentStruct { only_field: TransparentPrimitiveWrappingStructure } + +/// cbindgen:transparent-typedef +#[repr(transparent)] +struct ErasedTransparentWrappingTransparentNonNullPointerStruct { only_field: ErasedTransparentNonNullPointerWrappingStruct } + +// Transparent structure wrapping another type +#[repr(transparent)] +/// cbindgen:transparent-typedef +struct ErasedTransparentStructWrappingAnotherType { only_field: T } + +type TransparentIntStruct = ErasedTransparentStructWrappingAnotherType; +type TransparentComplexStruct = ErasedTransparentStructWrappingAnotherType; +type TransparentTransparentStruct = ErasedTransparentStructWrappingAnotherType; +type TransparentNonNullStruct = ErasedTransparentStructWrappingAnotherType>; +type TransparentOptionNonNullStruct = ErasedTransparentStructWrappingAnotherType>>; + +/// cbindgen:transparent-typedef +type ErasedTransparentIntStruct = ErasedTransparentStructWrappingAnotherType; +/// cbindgen:transparent-typedef +type ErasedTransparentComplexStruct = ErasedTransparentStructWrappingAnotherType; +/// cbindgen:transparent-typedef +type ErasedTransparentOptionNonNullStruct = ErasedTransparentStructWrappingAnotherType>>; + +#[no_mangle] +pub extern "C" fn erased_root( + a: ErasedTransparentNonNullPointerWrappingStruct, + b: ErasedTransparentOptionalNonNullPointerWrappingStruct, + c: ErasedTransparentWrappingAnotherTransparentStruct, + d: ErasedTransparentWrappingTransparentNonNullPointerStruct, + e: ErasedTransparentStructWrappingAnotherType, + f: ErasedTransparentStructWrappingAnotherType, + g: ErasedTransparentStructWrappingAnotherType, + h: ErasedTransparentStructWrappingAnotherType, + i: ErasedTransparentIntStruct, + j: TransparentIntStruct, + k: TransparentComplexStruct, + l: TransparentTransparentStruct, + m: TransparentNonNullStruct, + n: TransparentOptionNonNullStruct, ) { } diff --git a/tests/rust/transparent_typedef.rs b/tests/rust/transparent_typedef.rs new file mode 100644 index 000000000..efd07cfa7 --- /dev/null +++ b/tests/rust/transparent_typedef.rs @@ -0,0 +1,82 @@ +/// cbindgen:transparent-typedef +#[repr(transparent)] +pub struct Transparent { + field: T, + _phantom: std::marker::PhantomData

, +} + +/// cbindgen:transparent-typedef +pub type Alias = Transparent; + + +#[repr(C)] +pub struct AlwaysErased1 { + a: Alias, + n: NonNull, + t: Transparent, +} + +// Option only gets erased if T is NonNull or NonZero; use references so it still compiles. +#[repr(C)] +pub struct SometimesErased1 { + o: &Option, +} + +#[no_mangle] +pub extern "C" fn root1( + a: AlwaysErased1, + sn: SometimesErased1>, + sz: SometimesErased1>, + si: SometimesErased1) {} + +#[repr(C)] +pub struct AlwaysErased2 { + aa: Alias>, + an: Alias>, + at: Alias>, + na: NonNull>, + nn: NonNull>, + nt: NonNull>, + on: Option>, + ta: Transparent>, + tn: Transparent>, + tt: Transparent>, +} + +// Option only gets erased if T is NonNull or NonZero; use references so it still compiles. +#[repr(C)] +pub struct SometimesErased2 { + ao: &Alias>, + no: &NonNull>, + oa: &Option>, + ot: &Option>, + to: &Transparent>, +} + +// Use references so it still compiles. +#[repr(C)] +pub struct NeverErased2 { + oo: &Option>, +} + +#[no_mangle] +pub extern "C" fn root2( + a: AlwaysErased2, + sn: SometimesErased2>, + sz: SometimesErased2>, + si: SometimesErased2, + n: NeverErased2) {} + +#[repr(C)] +pub struct AlwaysErasedMany { + // A few erasable quadruplets + tont: Transparent>>>, + otnt: Option>>>, + totn: Transparent>>>, + + // One erasable quintuplet + totnt: Transparent>>>>, +} + +#[no_mangle] +pub extern "C" fn root_many(a: AlwaysErasedMany) {} From 42ae2fe652316507f428fb400a9184b83cb7e16d Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Mon, 12 Aug 2024 15:10:13 -0700 Subject: [PATCH 2/2] address review: simplify function type erasure logic --- src/bindgen/ir/ty.rs | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/src/bindgen/ir/ty.rs b/src/bindgen/ir/ty.rs index c396eeda6..78690ecaa 100644 --- a/src/bindgen/ir/ty.rs +++ b/src/bindgen/ir/ty.rs @@ -600,30 +600,24 @@ impl Type { is_nullable, never_return, } => { - // Predict that ret+args will all be erased, but decrement the count whenever we're - // wrong. If the count drops to 0, then type erasure was a no-op after all. - let mut num_erased = 1 + args.len(); - let erased_ret = eraser - .erase_transparent_types(library, ret, mappings) - .unwrap_or_else(|| { - num_erased -= 1; - ret.as_ref().clone() - }); - + // Attempt to erase ret and all args; if any of them were actually erased, then + // assemble and return the simplified function signature that results. + let mut erased_any = false; + let mut try_erase = |ty| { + if let Some(erased) = eraser.erase_transparent_types(library, ty, mappings) { + erased_any = true; + erased + } else { + ty.clone() + } + }; + let erased_ret = try_erase(ret); let erased_args = args .iter() - .map(|(name, ty)| { - let erased_ty = eraser - .erase_transparent_types(library, ty, mappings) - .unwrap_or_else(|| { - num_erased -= 1; - ty.clone() - }); - (name.clone(), erased_ty) - }) + .map(|(name, ty)| (name.clone(), try_erase(ty))) .collect(); - if num_erased > 0 { + if erased_any { return Some(Type::FuncPtr { ret: Box::new(erased_ret), args: erased_args,