Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .clippy.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Specify the minimum supported Rust version
msrv = "1.70"
msrv = "1.74"
10 changes: 10 additions & 0 deletions docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,16 @@ derive_tagged_enum_copy_assignment = false
# default: false
private_default_tagged_enum_constructor = false

# Whether to only output a single tag enum for generic tagged enums. This only
# applies when generics are being monomorphized (i.e. not C++).
#
# For example, an enum monomorph `COption<u8>` would normally generate a tag enum
# `COption_u8_Tag`, but with this option enabled all monomorphs of `COption<T>` will
# use the same tag enum, named `COption_Tag`.
#
# default: false
merge_generic_tags = false




Expand Down
3 changes: 3 additions & 0 deletions src/bindgen/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,8 @@ pub struct EnumConfig {
/// Whether to generate empty, private default-constructors for tagged
/// enums.
pub private_default_tagged_enum_constructor: bool,
/// Whether to only output a single tag enum for generic tagged enums.
pub merge_generic_tags: bool,
}

impl Default for EnumConfig {
Expand All @@ -634,6 +636,7 @@ impl Default for EnumConfig {
derive_ostream: false,
enum_class: true,
private_default_tagged_enum_constructor: false,
merge_generic_tags: false,
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions src/bindgen/dependencies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use std::collections::HashSet;

use crate::bindgen::ir::{ItemContainer, Path};

use super::library::Library;

/// A dependency list is used for gathering what order to output the types.
#[derive(Default)]
pub struct Dependencies {
Expand All @@ -22,6 +24,24 @@ impl Dependencies {
}
}

pub fn add_path(&mut self, library: &Library, path: &Path) {
if let Some(items) = library.get_items(path) {
if !self.items.contains(path) {
self.items.insert(path.clone());

for item in &items {
item.deref().add_dependencies(library, self);
}
self.order.extend(items);
}
} else {
warn!(
"Can't find {}. This usually means that this type was incompatible or not found.",
path
);
}
}

pub fn sort(&mut self) {
// Sort untagged enums and opaque structs into their own layers because they don't
// depend on each other or anything else.
Expand Down
79 changes: 69 additions & 10 deletions src/bindgen/ir/enumeration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ pub struct Enum {
pub repr: Repr,
pub variants: Vec<EnumVariant>,
pub tag: Option<String>,
pub external_tag: bool,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
Expand Down Expand Up @@ -415,6 +416,7 @@ impl Enum {
repr,
variants,
tag,
/* external_tag */ false,
Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
annotations,
Documentation::load(&item.attrs),
Expand All @@ -428,6 +430,7 @@ impl Enum {
repr: Repr,
variants: Vec<EnumVariant>,
tag: Option<String>,
external_tag: bool,
cfg: Option<Cfg>,
annotations: AnnotationSet,
documentation: Documentation,
Expand All @@ -440,6 +443,7 @@ impl Enum {
repr,
variants,
tag,
external_tag,
cfg,
annotations,
documentation,
Expand Down Expand Up @@ -502,11 +506,18 @@ impl Item for Enum {
}

fn rename_for_config(&mut self, config: &Config) {
config.export.rename(&mut self.export_name);
// Check if the export_name already contains the prefix to avoid double prefixing
if let Some(ref prefix) = config.export.prefix {
if !self.export_name.starts_with(prefix) {
config.export.rename(&mut self.export_name);
}
} else {
config.export.rename(&mut self.export_name);
}

if config.language != Language::Cxx && self.tag.is_some() {
if config.language != Language::Cxx && self.tag.is_some() && !self.external_tag {
// it makes sense to always prefix Tag with type name in C
let new_tag = format!("{}_Tag", self.export_name);
let new_tag = format!("{}_Tag", self.export_name());
if self.repr.style == ReprStyle::Rust {
for variant in &mut self.variants {
if let VariantBody::Body { ref mut body, .. } = variant.body {
Expand Down Expand Up @@ -540,18 +551,18 @@ impl Item for Enum {
.bool("prefix-with-name")
.unwrap_or(config.enumeration.prefix_with_name)
{
let prefix = self.export_name.trim_end_matches("_Tag");

let separator = if config.export.mangle.remove_underscores {
""
} else {
"_"
};

for variant in &mut self.variants {
variant.export_name =
format!("{}{}{}", self.export_name, separator, variant.export_name);
variant.export_name = format!("{}{}{}", prefix, separator, variant.export_name);
if let VariantBody::Body { ref mut body, .. } = variant.body {
body.export_name =
format!("{}{}{}", self.export_name, separator, body.export_name());
body.export_name = format!("{}{}{}", prefix, separator, body.export_name());
}
}
}
Expand Down Expand Up @@ -603,6 +614,39 @@ impl Item for Enum {
library: &Library,
out: &mut Monomorphs,
) {
let config = library.get_config();
// Only use external tags for generic enums when merge_generic_tags is enabled
let external_tag = config.enumeration.merge_generic_tags && !generic_values.is_empty();

let tag = if external_tag {
// Calculate what the prefixed tag name should be
let mut prefixed_export_name = self.export_name().to_string();
config.export.rename(&mut prefixed_export_name);
let final_tag = format!("{}_Tag", prefixed_export_name);

if !out.contains(&GenericPath::new(self.path.clone(), vec![])) {
// Create the shared tag enum with the final prefixed name
let tag_path = Path::new(final_tag.clone());
let tag_enum = Enum::new(
tag_path,
GenericParams::default(),
self.repr,
self.variants.clone(),
None,
false,
self.cfg.clone(),
self.annotations.clone(),
self.documentation.clone(),
);

out.insert_enum(library, self, tag_enum, vec![]);
}

Some(final_tag)
} else {
self.tag.clone()
};

let mappings = self.generic_params.call(self.path.name(), generic_values);

for variant in &self.variants {
Expand All @@ -625,7 +669,8 @@ impl Item for Enum {
.iter()
.map(|v| v.specialize(generic_values, &mappings, library.get_config()))
.collect(),
self.tag.clone(),
tag,
external_tag,
self.cfg.clone(),
self.annotations.clone(),
self.documentation.clone(),
Expand All @@ -635,6 +680,15 @@ impl Item for Enum {
}

fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
if self.external_tag {
if let Some(tag) = self.tag.clone() {
let path = Path::new(tag);

// If there is an external tag enum, then add it as a dependency.
out.add_path(library, &path);
}
}

for variant in &self.variants {
variant.add_dependencies(library, out);
}
Expand Down Expand Up @@ -771,6 +825,11 @@ impl Enum {
}
}

// Add a newline after the tag enum to ensure proper spacing
if size.is_some() || config.language == Language::Cython {
out.new_line();
}

// Emit convenience methods for the tag enum.
self.write_derived_functions_enum(config, language_backend, out);
}
Expand Down Expand Up @@ -830,8 +889,6 @@ impl Enum {
..
} = variant.body
{
out.new_line();
out.new_line();
let condition = variant.cfg.to_condition(config);
// Cython doesn't support conditional enum variants.
if config.language != Language::Cython {
Expand All @@ -841,6 +898,8 @@ impl Enum {
if config.language != Language::Cython {
condition.write_after(config, out);
}
out.new_line();
out.new_line();
}
}
}
Expand Down
19 changes: 1 addition & 18 deletions src/bindgen/ir/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -701,24 +701,7 @@ impl Type {
}
let path = generic.path();
if !generic_params.iter().any(|param| param.name() == path) {
if let Some(items) = library.get_items(path) {
if !out.items.contains(path) {
out.items.insert(path.clone());

for item in &items {
item.deref().add_dependencies(library, out);
}
for item in items {
out.order.push(item);
}
}
} else {
warn!(
"Can't find {}. This usually means that this type was incompatible or \
not found.",
path
);
}
out.add_path(library, path);
}
}
Type::Primitive(_) => {}
Expand Down
5 changes: 4 additions & 1 deletion src/bindgen/language_backend/clike.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,10 @@ impl LanguageBackend for CLikeLanguageBackend<'_> {
}

// Emit the tag enum and everything related to it.
e.write_tag_enum(self.config, self, out, size, Self::write_enum_variant);
// Skip writing the tag enum if this is a specialized monomorph with an external tag
if !e.external_tag {
e.write_tag_enum(self.config, self, out, size, Self::write_enum_variant);
}

// If the enum has data, we need to emit structs for the variants and gather them together.
if has_data {
Expand Down
5 changes: 4 additions & 1 deletion src/bindgen/language_backend/cython.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ impl LanguageBackend for CythonLanguageBackend<'_> {
self.write_documentation(out, &e.documentation);

// Emit the tag enum and everything related to it.
e.write_tag_enum(self.config, self, out, size, Self::write_enum_variant);
// Skip writing the tag enum if this is a specialized monomorph with an external tag
if !e.external_tag {
e.write_tag_enum(self.config, self, out, size, Self::write_enum_variant);
}

// If the enum has data, we need to emit structs for the variants and gather them together.
if has_data {
Expand Down
1 change: 1 addition & 0 deletions template.toml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ derive_tagged_enum_destructor = false
derive_tagged_enum_copy_constructor = false
enum_class = true
private_default_tagged_enum_constructor = false
merge_generic_tags = false



Expand Down
3 changes: 3 additions & 0 deletions tests/expectations-symbols/merge_generic_tags.c.sym
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
root;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
process_result;
process_str_result;
get_option_int;
get_option_str;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
root;
};
1 change: 1 addition & 0 deletions tests/expectations/alias.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ enum Status {
};
typedef uint32_t Status;


typedef struct {
int32_t a;
float b;
Expand Down
1 change: 1 addition & 0 deletions tests/expectations/alias.compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ enum Status
typedef uint32_t Status;
#endif // __cplusplus


typedef struct {
int32_t a;
float b;
Expand Down
1 change: 1 addition & 0 deletions tests/expectations/alias.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ enum class Status : uint32_t {
Err,
};


struct Dep {
int32_t a;
float b;
Expand Down
1 change: 1 addition & 0 deletions tests/expectations/alias.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cdef extern from *:
Err,
ctypedef uint32_t Status;


ctypedef struct Dep:
int32_t a;
float b;
Expand Down
1 change: 1 addition & 0 deletions tests/expectations/alias_both.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ enum Status {
};
typedef uint32_t Status;


typedef struct Dep {
int32_t a;
float b;
Expand Down
1 change: 1 addition & 0 deletions tests/expectations/alias_both.compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ enum Status
typedef uint32_t Status;
#endif // __cplusplus


typedef struct Dep {
int32_t a;
float b;
Expand Down
1 change: 1 addition & 0 deletions tests/expectations/alias_tag.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ enum Status {
};
typedef uint32_t Status;


struct Dep {
int32_t a;
float b;
Expand Down
1 change: 1 addition & 0 deletions tests/expectations/alias_tag.compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ enum Status
typedef uint32_t Status;
#endif // __cplusplus


struct Dep {
int32_t a;
float b;
Expand Down
1 change: 1 addition & 0 deletions tests/expectations/alias_tag.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cdef extern from *:
Err,
ctypedef uint32_t Status;


cdef struct Dep:
int32_t a;
float b;
Expand Down
Loading