Skip to content

Commit

Permalink
Handle unions in header-translator
Browse files Browse the repository at this point in the history
Unions are used a lot in more low-level frameworks like Security that we
want to support in the future.

This also "fixes" a bug where `CFStringBuiltInEncodings` was merged with
`CFStringEncoding`, at least so that its mapping is the same as Swift's.
  • Loading branch information
madsmtm committed Jan 12, 2025
1 parent c7e42b6 commit d4a7c9f
Show file tree
Hide file tree
Showing 22 changed files with 249 additions and 218 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ categories = [
[workspace.lints.rust]
elided_lifetimes_in_paths = "warn"
missing_copy_implementations = "warn"
missing_debug_implementations = "warn"
non_ascii_idents = "deny"
unreachable_pub = "warn"
unsafe_op_in_unsafe_fn = "deny"
Expand Down
1 change: 1 addition & 0 deletions crates/block2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@
#![no_std]
#![warn(missing_docs)]
#![warn(missing_debug_implementations)]
#![warn(clippy::missing_errors_doc)]
#![warn(clippy::missing_panics_doc)]
// Update in Cargo.toml as well.
Expand Down
1 change: 1 addition & 0 deletions crates/dispatch2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#![no_std]
#![allow(unreachable_patterns)]
#![warn(missing_docs)]
#![warn(missing_debug_implementations)]
#![warn(clippy::undocumented_unsafe_blocks)]
#![warn(clippy::missing_safety_doc)]
// Update in Cargo.toml as well.
Expand Down
5 changes: 5 additions & 0 deletions crates/dispatch2/translation-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ typedef.dispatch_io_interval_flags_t.skipped = true
# Lonely enum constant that we want to tie to dispatch_time_t
enum.anonymous.constants.DISPATCH_WALLTIME_NOW.skipped = true

# `dispatch_object_t` is a special union that declares all the different kinds
# of dispatch objects, but that won't work in Rust.
union.dispatch_object_t.skipped = true
typedef.dispatch_object_t.skipped = true

# We want to implement TryFrom on this
typedef.dispatch_time_t.skipped = true

Expand Down
7 changes: 5 additions & 2 deletions crates/header-translator/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,10 @@ pub struct LibraryConfig {
pub protocol_data: HashMap<String, ProtocolData>,
#[serde(rename = "struct")]
#[serde(default)]
pub struct_data: HashMap<String, StructData>,
pub struct_data: HashMap<String, RecordData>,
#[serde(rename = "union")]
#[serde(default)]
pub union_data: HashMap<String, RecordData>,
#[serde(rename = "enum")]
#[serde(default)]
pub enum_data: HashMap<String, EnumData>,
Expand Down Expand Up @@ -299,7 +302,7 @@ pub struct ProtocolData {

#[derive(Deserialize, Debug, Default, Clone, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct StructData {
pub struct RecordData {
#[serde(default)]
pub skipped: bool,
}
Expand Down
3 changes: 1 addition & 2 deletions crates/header-translator/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use crate::availability::Availability;
use crate::context::MacroLocation;
use crate::name_translation::enum_prefix;
use crate::rust_type::Ty;
use crate::stmt::new_enum_id;
use crate::unexposed_attr::UnexposedAttr;
use crate::{immediate_children, Context, ItemIdentifier};

Expand Down Expand Up @@ -223,7 +222,7 @@ impl Expr {
.get_semantic_parent()
.expect("EnumConstantDecl parent");
assert_eq!(parent.get_kind(), EntityKind::EnumDecl);
let parent_id = new_enum_id(&parent, context);
let parent_id = ItemIdentifier::new_optional(&parent, context);
let variant = entity.get_name().expect("EnumConstantDecl name");
if parent_id.name.is_some() {
let parent_id = parent_id.map_name(|name| name.unwrap());
Expand Down
35 changes: 19 additions & 16 deletions crates/header-translator/src/global_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,33 +116,36 @@ fn update_module(module: &mut Module, cf_type_id_mapping: &BTreeMap<String, Item

let mut iter = mem::take(&mut module.stmts).into_iter().peekable();
while let Some(mut stmt) = iter.next() {
// Fix up a few typedef + enum declarations
if let Stmt::AliasDecl {
id, ty, kind: None, ..
} = &stmt
{
if let Some(Stmt::EnumDecl {
id: enum_id,
ty: enum_ty,
// Fix up a few enum + typedef declarations. Example:
//
// typedef enum _SecureDownloadTrustCallbackResult {
// kSecureDownloadDoNotEvaluateSigner = 0,
// kSecureDownloadEvaluateSigner = 1,
// kSecureDownloadFailEvaluation = 2
// } SecureDownloadTrustCallbackResult;
if let Stmt::EnumDecl { id, .. } = &mut stmt {
if let Some(Stmt::AliasDecl {
id: typedef_id,
ty,
kind: None,
..
}) = iter.peek_mut()
}) = iter.peek()
{
if enum_ty.is_typedef_to(&id.name) {
*enum_id = id.clone();
*enum_ty = ty.clone();
if ty.is_enum(&id.name) && typedef_id.name == id.name.trim_start_matches("_") {
*id = typedef_id.clone();
// Skip adding the now-redundant alias to the list of statements
continue;
let _ = iter.next();
}
}
}

// Fix up a few struct + typedef declarations. Example:
// Fix up a few struct/union + typedef declarations. Example:
//
// typedef struct _AVBeatRange {
// AVMusicTimeStamp start;
// AVMusicTimeStamp length;
// } AVBeatRange;
if let Stmt::StructDecl {
if let Stmt::RecordDecl {
id, encoding_name, ..
} = &mut stmt
{
Expand All @@ -153,7 +156,7 @@ fn update_module(module: &mut Module, cf_type_id_mapping: &BTreeMap<String, Item
..
}) = iter.peek()
{
if ty.is_struct(&id.name) && typedef_id.name == id.name.trim_start_matches("_") {
if ty.is_record(&id.name) && typedef_id.name == id.name.trim_start_matches("_") {
if encoding_name.is_none() {
*encoding_name = Some(id.name.clone());
}
Expand Down
23 changes: 22 additions & 1 deletion crates/header-translator/src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,28 @@ impl ItemIdentifier {

impl ItemIdentifier<Option<String>> {
pub fn new_optional(entity: &Entity<'_>, context: &Context<'_>) -> Self {
Self::with_name(entity.get_name(), entity, context)
let mut id = Self::with_name(entity.get_name(), entity, context);

// union (unnamed at /Applications/Xcode.app/...)
// enum (unnamed at /Applications/Xcode.app/...)
if id
.name
.as_deref()
.map(|name| name.contains(" (unnamed at"))
.unwrap_or(false)
{
id.name = None;
}

id
}

pub fn to_option(self) -> Option<ItemIdentifier> {
if let Some(name) = self.name {
Some(ItemIdentifier::from_raw(name, self.location))
} else {
None
}
}
}

Expand Down
112 changes: 84 additions & 28 deletions crates/header-translator/src/rust_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,11 @@ pub enum Ty {
/// Whether the struct's declaration has a bridge attribute.
is_bridged: bool,
},
Union {
id: ItemIdentifier<Option<String>>,
/// FIXME: This does not work for recursive structs.
fields: Vec<Ty>,
},
Fn {
is_variadic: bool,
no_escape: bool,
Expand Down Expand Up @@ -621,21 +626,36 @@ impl Ty {
TypeKind::Double => Self::Primitive(Primitive::Double),
TypeKind::Record => {
let declaration = ty.get_declaration().expect("record declaration");
Self::Struct {
id: ItemIdentifier::new(&declaration, context),
fields: ty
.get_fields()
.expect("struct fields")
.into_iter()
.map(|field| {
Self::parse(
field.get_type().expect("struct field type"),
Lifetime::Unspecified,
context,
)
})
.collect(),
is_bridged: is_bridged(&declaration, context),

let fields = ty
.get_fields()
.expect("struct fields")
.into_iter()
.map(|field| {
Self::parse(
field.get_type().expect("struct field type"),
Lifetime::Unspecified,
context,
)
})
.collect();

match declaration.get_kind() {
EntityKind::StructDecl => Self::Struct {
id: ItemIdentifier::new(&declaration, context),
fields,
is_bridged: is_bridged(&declaration, context),
},
EntityKind::UnionDecl => Self::Union {
id: ItemIdentifier::new_optional(&declaration, context),
fields,
},
_ => {
error!(?declaration, "unknown record type decl");
Self::GenericParam {
name: "UnknownRecord".into(),
}
}
}
}
TypeKind::Enum => {
Expand Down Expand Up @@ -1226,6 +1246,16 @@ impl Ty {
items.push(id.clone());
items
}
Self::Union { id, fields, .. } => {
let mut items = Vec::new();
for field in fields {
items.extend(field.required_items());
}
if let Some(id) = id.clone().to_option() {
items.push(id);
}
items
}
Self::Fn {
is_variadic: _,
no_escape: _,
Expand Down Expand Up @@ -1291,7 +1321,7 @@ impl Ty {
element_type.requires_mainthreadmarker(self_requires)
}
Self::Enum { ty, .. } => ty.requires_mainthreadmarker(self_requires),
Self::Struct { fields, .. } => fields
Self::Struct { fields, .. } | Self::Union { fields, .. } => fields
.iter()
.any(|field| field.requires_mainthreadmarker(self_requires)),
Self::Fn {
Expand Down Expand Up @@ -1347,6 +1377,9 @@ impl Ty {
Self::Struct { fields, .. } => fields
.iter()
.any(|field| field.provides_mainthreadmarker(self_provides)),
Self::Union { fields, .. } => fields
.iter()
.all(|field| field.provides_mainthreadmarker(self_provides)),
_ => false,
}
}
Expand Down Expand Up @@ -1445,6 +1478,21 @@ impl Ty {
matches!(self, Self::TypeDef { id, .. } if id.name == "CFTypeID")
}

pub(crate) fn contains_union(&self) -> bool {
match self {
Self::Union { .. } => true,
Self::TypeDef { id, .. }
if matches!(&*id.name, "MPSPackedFloat3" | "MTLPackedFloat3") =>
{
// These are custom-defined to not contain the internal union.
false
}
Self::TypeDef { to, .. } => to.contains_union(),
Self::Struct { fields, .. } => fields.iter().any(|field| field.contains_union()),
_ => false,
}
}

pub(crate) fn is_objc_bool(&self) -> bool {
match self {
Self::Primitive(Primitive::ObjcBool) => true,
Expand Down Expand Up @@ -1547,6 +1595,14 @@ impl Ty {
Self::Struct { id, .. } => {
write!(f, "{}", id.path())
}
Self::Union { id, .. } => {
if let Some(id) = id.clone().to_option() {
write!(f, "{}", id.path())
} else {
// TODO
write!(f, "UnknownUnion")
}
}
Self::Enum { id, .. } => {
write!(f, "{}", id.path())
}
Expand Down Expand Up @@ -2032,7 +2088,7 @@ impl Ty {
})
}

pub(crate) fn struct_(&self) -> impl fmt::Display + '_ {
pub(crate) fn record(&self) -> impl fmt::Display + '_ {
FormatterFn(move |f| match self {
Self::Array {
element_type,
Expand All @@ -2056,7 +2112,7 @@ impl Ty {
false
}

pub(crate) fn struct_encoding(&self) -> impl fmt::Display + '_ {
pub(crate) fn record_encoding(&self) -> impl fmt::Display + '_ {
FormatterFn(move |f| match self {
Self::Primitive(Primitive::C99Bool) => write!(f, "Encoding::Bool"),
Self::Primitive(Primitive::Long) => write!(f, "Encoding::C_LONG"),
Expand All @@ -2065,7 +2121,7 @@ impl Ty {
Self::TypeDef { to, .. } if to.fn_contains_bool_argument() => {
write!(f, "Encoding::Pointer(&Encoding::Unknown)")
}
_ => write!(f, "<{}>::ENCODING", self.struct_()),
_ => write!(f, "<{}>::ENCODING", self.record()),
})
}

Expand Down Expand Up @@ -2193,10 +2249,6 @@ impl Ty {
Self::parse(ty, Lifetime::Unspecified, context)
}

pub(crate) fn is_enum(&self) -> bool {
matches!(self, Self::Enum { .. })
}

pub(crate) fn pointer_to_opaque_struct_or_void(&self) -> Option<Option<&str>> {
if let Self::Pointer {
pointee,
Expand Down Expand Up @@ -2273,7 +2325,7 @@ impl Ty {
ty
}

pub(crate) fn parse_struct_field(ty: Type<'_>, context: &Context<'_>) -> Self {
pub(crate) fn parse_record_field(ty: Type<'_>, context: &Context<'_>) -> Self {
Self::parse(ty, Lifetime::Unspecified, context)
}

Expand Down Expand Up @@ -2362,12 +2414,16 @@ impl Ty {
matches!(self, Self::Pointer { pointee, .. } if **pointee == Self::Self_)
}

pub(crate) fn is_typedef_to(&self, s: &str) -> bool {
matches!(self, Self::TypeDef { id, .. } if id.name == s)
pub(crate) fn is_enum(&self, s: &str) -> bool {
matches!(self, Self::Enum { id, .. } if id.name == s)
}

pub(crate) fn is_struct(&self, s: &str) -> bool {
matches!(self, Self::Struct { id, .. } if id.name == s)
pub(crate) fn is_record(&self, s: &str) -> bool {
match self {
Self::Struct { id, .. } if id.name == s => true,
Self::Union { id, .. } if id.name.as_deref() == Some(s) => true,
_ => false,
}
}

pub(crate) fn is_enum_through_typedef(&self) -> bool {
Expand Down
Loading

0 comments on commit d4a7c9f

Please sign in to comment.