Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for transparent typedefs (take 2) #1029

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
103 changes: 85 additions & 18 deletions docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> {
ptr: NonNull<T>,
}

pub struct Foo { }

#[no_mangle]
pub extern "C" fn foo_operation(foo: Option<Handle<Foo>>) { }
```

By default, the exported C++ code would fail to compile, because the function takes `Option<...>`
(which is an opaque type) by value:

```cpp
template<typename T>
struct Option<T>;

template<typename T>
using Handle = T;

struct Foo;

void foo_operation(Option<Handle<Foo>> foo);
```

If we annotate `Handle` with `transparent-typedef` (leaving the rest of the code unchanged):
```rust
/// cbindgen:transparent-typedef
#[repr(transparent)]
pub struct Handle<T> {
ptr: NonNull<T>,
}
```

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<Handle<Foo>>;

#[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:

Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/bindgen/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
13 changes: 13 additions & 0 deletions src/bindgen/ir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::bindgen::ir::{
};
use crate::bindgen::language_backend::LanguageBackend;
use crate::bindgen::library::Library;
use crate::bindgen::transparent::ResolveTransparentTypes;
use crate::bindgen::writer::SourceWriter;
use crate::bindgen::Bindings;

Expand Down Expand Up @@ -605,6 +606,18 @@ impl Item for Constant {
}
}

impl ResolveTransparentTypes for Constant {
fn resolve_transparent_types(&self, library: &Library) -> Option<Self> {
// 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.
Some(Constant {
ty: self.ty.transparent_alias(library, GenericParams::empty())?,
..self.clone()
})
}
}

impl Constant {
pub fn write_declaration<F: Write, LB: LanguageBackend>(
&self,
Expand Down
59 changes: 47 additions & 12 deletions src/bindgen/ir/enumeration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* 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 std::io::Write;

use syn::ext::IdentExt;
Expand All @@ -20,6 +21,7 @@ use crate::bindgen::mangle;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::rename::{IdentifierType, RenameRule};
use crate::bindgen::reserved;
use crate::bindgen::transparent::{CowIsOwned, IterCow, ResolveTransparentTypes};
use crate::bindgen::writer::{ListType, SourceWriter};

#[allow(clippy::large_enum_variant)]
Expand Down Expand Up @@ -248,12 +250,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);
Expand Down Expand Up @@ -641,6 +637,51 @@ impl Item for Enum {
}
}

impl ResolveTransparentTypes for Enum {
fn resolve_transparent_types(&self, library: &Library) -> Option<Self> {
// If the enum uses inline tag fields, every variant struct has one. Skip resolving them.
let params = Self::resolve_generic_params(library, &self.generic_params);
let skip_inline_tag_field = Self::inline_tag_field(&self.repr);
let variants: Vec<_> = self
.variants
.iter()
.cow_map(|v| match v.body {
VariantBody::Body {
ref name,
ref body,
inline,
inline_casts,
} => {
let fields =
Self::resolve_fields(library, &body.fields, &params, skip_inline_tag_field);
let Cow::Owned(fields) = fields else {
return None;
};
Some(EnumVariant {
body: VariantBody::Body {
name: name.clone(),
body: Struct {
fields,
..body.clone()
},
inline,
inline_casts,
},
..v.clone()
})
}
VariantBody::Empty(..) => None,
})
.collect();

(params.cow_is_owned() || variants.iter().any_owned()).then(|| Enum {
generic_params: params.into_owned(),
variants: variants.into_iter().map(Cow::into_owned).collect(),
..self.clone()
})
}
}

impl Enum {
/// Emit the tag enum and convenience methods for it.
/// For enums with data this is only a part of the output,
Expand Down Expand Up @@ -1496,10 +1537,4 @@ impl Enum {
}
}
}

pub fn simplify_standard_types(&mut self, config: &Config) {
for variant in &mut self.variants {
variant.simplify_standard_types(config);
}
}
}
36 changes: 28 additions & 8 deletions src/bindgen/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@
* 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 std::collections::HashMap;

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, GenericParams, GenericPath, Path, Type,
};
use crate::bindgen::library::Library;
use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::rename::{IdentifierType, RenameRule};
use crate::bindgen::reserved;
use crate::bindgen::transparent::{CowIsOwned, IterCow, ResolveTransparentTypes};
use crate::bindgen::utilities::IterHelpers;

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -115,13 +119,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 {
Expand Down Expand Up @@ -219,6 +216,29 @@ impl Function {
}
}

impl ResolveTransparentTypes for Function {
fn resolve_transparent_types(&self, library: &Library) -> Option<Function> {
let empty = GenericParams::empty();
let ret = self.ret.transparent_alias_cow(library, empty);
let args: Vec<_> = self
.args
.iter()
.cow_map(|arg| {
Some(FunctionArgument {
ty: arg.ty.transparent_alias(library, empty)?,
..arg.clone()
})
})
.collect();

(ret.cow_is_owned() || args.iter().any_owned()).then(|| Function {
ret: ret.into_owned(),
args: args.into_iter().map(Cow::into_owned).collect(),
..self.clone()
})
}
}

trait SynFnArgHelpers {
fn as_argument(&self) -> Result<Option<FunctionArgument>, String>;
}
Expand Down
7 changes: 5 additions & 2 deletions src/bindgen/ir/generic_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ pub struct GenericParam {
}

impl GenericParam {
pub fn new_type_param(name: &str) -> Self {
pub fn new_type_param(name: &str, default: Option<GenericArgument>) -> Self {
GenericParam {
name: Path::new(name),
ty: GenericParamType::Type,
default: None,
default,
}
}

Expand Down Expand Up @@ -80,6 +80,9 @@ impl GenericParam {
pub fn name(&self) -> &Path {
&self.name
}
pub fn default(&self) -> Option<&GenericArgument> {
self.default.as_ref()
}
}

#[derive(Default, Debug, Clone)]
Expand Down
14 changes: 10 additions & 4 deletions src/bindgen/ir/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::bindgen::ir::{
AnnotationSet, Cfg, Documentation, GenericParams, Item, ItemContainer, Path, Type,
};
use crate::bindgen::library::Library;
use crate::bindgen::transparent::ResolveTransparentTypes;

#[derive(Debug, Clone)]
pub struct Static {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -113,3 +110,12 @@ impl Item for Static {
self.ty.add_dependencies(library, out);
}
}

impl ResolveTransparentTypes for Static {
fn resolve_transparent_types(&self, library: &Library) -> Option<Self> {
Some(Static {
ty: self.ty.transparent_alias(library, GenericParams::empty())?,
..self.clone()
})
}
}
6 changes: 4 additions & 2 deletions src/bindgen/ir/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ pub enum ItemContainer {
Typedef(Typedef),
}

impl ItemContainer {
pub fn deref(&self) -> &dyn Item {
impl std::ops::Deref for ItemContainer {
type Target = dyn Item + 'static;

fn deref(&self) -> &Self::Target {
match *self {
ItemContainer::Constant(ref x) => x,
ItemContainer::Static(ref x) => x,
Expand Down
Loading
Loading