From 276f2398b167b42db7dec3fad8aa924b1a554206 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 17 Dec 2024 12:58:39 +0100 Subject: [PATCH] Better support Dispatch in header-translator --- crates/header-translator/src/availability.rs | 3 ++ crates/header-translator/src/id.rs | 7 ++- crates/header-translator/src/main.rs | 1 + crates/header-translator/src/rust_type.rs | 50 +++++++++++++------ crates/header-translator/src/stmt.rs | 43 ++++++++++++++-- .../header-translator/src/unexposed_attr.rs | 18 +++++-- 6 files changed, 97 insertions(+), 25 deletions(-) diff --git a/crates/header-translator/src/availability.rs b/crates/header-translator/src/availability.rs index 3a08b6086..9b81d8141 100644 --- a/crates/header-translator/src/availability.rs +++ b/crates/header-translator/src/availability.rs @@ -213,6 +213,9 @@ impl Availability { "swift" => { _swift = Some(availability); } + "driverkit" => { + // Ignore + } platform if platform.ends_with("_app_extension") => { // Ignore availability attributes for app extensions } diff --git a/crates/header-translator/src/id.rs b/crates/header-translator/src/id.rs index 4679fea91..12dc29fab 100644 --- a/crates/header-translator/src/id.rs +++ b/crates/header-translator/src/id.rs @@ -135,14 +135,17 @@ impl Location { // Redefined in the framework crate itself. "Darwin.MacTypes" => vec!["System".into()], - // Built-in + // Built-ins "DarwinFoundation.types.machine_types" => vec!["System".into()], + "_Builtin_stdarg.va_list" => vec!["System".into()], // Libc name if name.starts_with("sys_types") => vec!["libc".into()], "DarwinFoundation.types.sys_types" => vec!["libc".into()], + "DarwinFoundation.qos" => vec!["libc".into()], name if name.starts_with("Darwin.POSIX") => vec!["libc".into()], "_stdio" => vec!["libc".into()], + "_time.timespec" => vec!["libc".into()], // Will be moved to the `mach2` crate in `libc` v1.0 name if name.starts_with("Darwin.Mach") => vec!["libc".into()], @@ -324,7 +327,7 @@ impl ItemIdentifier { pub fn with_name(name: N, entity: &Entity<'_>, _context: &Context<'_>) -> Self { let file = entity .get_location() - .expect("entity location") + .unwrap_or_else(|| panic!("no entity location: {entity:?}")) .get_expansion_location() .file .expect("expanded location file"); diff --git a/crates/header-translator/src/main.rs b/crates/header-translator/src/main.rs index 4f4f3ea8b..4eceaac57 100644 --- a/crates/header-translator/src/main.rs +++ b/crates/header-translator/src/main.rs @@ -353,6 +353,7 @@ fn get_translation_unit<'i: 'c, 'c>( // "usr/include/TargetConditionals.modulemap" // "System/Library/Frameworks/CoreFoundation.framework/Modules/module.modulemap" // "usr/include/ObjectiveC.modulemap" + // "usr/include/dispatch.modulemap" let modulemap = format!("System/Library/Frameworks/{framework}.framework/Modules/module.modulemap"); // On Mac Catalyst, we need to try to load from System/iOSSupport first. diff --git a/crates/header-translator/src/rust_type.rs b/crates/header-translator/src/rust_type.rs index e5e1b66ed..400797117 100644 --- a/crates/header-translator/src/rust_type.rs +++ b/crates/header-translator/src/rust_type.rs @@ -251,6 +251,7 @@ pub enum Primitive { ISize, USize, PtrDiff, + VaList, // Objective-C ObjcBool, NSInteger, @@ -309,6 +310,7 @@ impl Primitive { Self::ISize => "isize", // TODO: Use core::ffi::c_size_t Self::USize => "usize", + Self::VaList => "VaList", // TODO: Use core::ffi::c_ptr_diff_t Self::PtrDiff => "isize", Self::ObjcBool => "Bool", @@ -339,14 +341,14 @@ impl ItemRef { self.required_items.clone() } - fn new(entity: &Entity<'_>, context: &Context<'_>) -> Self { - let entity = entity + fn new(entity_ref: &Entity<'_>, context: &Context<'_>) -> Self { + let entity = entity_ref .get_location() .expect("itemref location") .get_entity() .expect("itemref entity"); - let id = ItemIdentifier::new(&entity, context); + let mut id = ItemIdentifier::new(&entity, context); if let Some(external) = context.library(id.library_name()).external.get(&id.name) { let id = ItemIdentifier::from_raw( @@ -375,26 +377,39 @@ impl ItemRef { }) .chain(iter::once(id.clone())) .collect(); - Self { + return Self { id, thread_safety, required_items, - } - } else if matches!( - entity.get_kind(), - EntityKind::ObjCInterfaceDecl | EntityKind::ObjCProtocolDecl - ) { - Self { + }; + } + + match entity.get_kind() { + EntityKind::ObjCInterfaceDecl | EntityKind::ObjCProtocolDecl => Self { id, thread_safety: ThreadSafety::from_decl(&entity, context), required_items: items_required_by_decl(&entity, context), + }, + EntityKind::MacroExpansion => { + id.name = entity_ref.get_name().unwrap_or_else(|| { + error!(?entity_ref, ?entity, "macro ref did not have name"); + id.name + }); + Self { + id: id.clone(), + // We cannot get thread safety from macro expansions + thread_safety: ThreadSafety::dummy(), + // Similarly, we cannot get for required items + required_items: vec![id], + } } - } else { - error!(?entity, "could not get declaration. Add appropriate external.{}.module = \"...\" to translation-config.toml", id.name); - Self { - id, - thread_safety: ThreadSafety::dummy(), - required_items: vec![], + _ => { + error!(?entity, "could not get declaration. Add appropriate external.{}.module = \"...\" to translation-config.toml", id.name); + Self { + id: id.clone(), + thread_safety: ThreadSafety::dummy(), + required_items: vec![id], + } } } } @@ -984,6 +999,9 @@ impl Ty { "intptr_t" => return Self::Primitive(Primitive::ISize), "uintptr_t" => return Self::Primitive(Primitive::USize), + // Varargs, still unsupported by Rust. + "__builtin_va_list" => return Self::Primitive(Primitive::VaList), + // MacTypes.h "UInt8" => return Self::Primitive(Primitive::U8), "UInt16" => return Self::Primitive(Primitive::U16), diff --git a/crates/header-translator/src/stmt.rs b/crates/header-translator/src/stmt.rs index d8f013931..1498dd330 100644 --- a/crates/header-translator/src/stmt.rs +++ b/crates/header-translator/src/stmt.rs @@ -529,6 +529,7 @@ pub enum Stmt { body: Option<()>, safe: bool, must_use: bool, + link_name: Option, }, /// typedef Type TypedefName; AliasDecl { @@ -1059,7 +1060,10 @@ impl Stmt { | EntityKind::ParmDecl | EntityKind::EnumDecl | EntityKind::IntegerLiteral => {} - _ => error!("unknown"), + EntityKind::ObjCIndependentClass => { + // TODO: Might be interesting? + } + _ => error!("unknown typedef child"), }); let ty = entity @@ -1212,7 +1216,19 @@ impl Stmt { immediate_children(&entity, |entity, _span| match entity.get_kind() { EntityKind::UnexposedAttr => { if let Some(attr) = UnexposedAttr::parse(&entity, context) { - error!(?attr, "unknown attribute on enum constant"); + match attr { + UnexposedAttr::Enum + | UnexposedAttr::Options + | UnexposedAttr::ClosedEnum + | UnexposedAttr::ErrorEnum => { + if kind.as_ref() != Some(&attr) { + error!(?kind, ?attr, "enum child had attribute that parent did not"); + } + } + attr => { + error!(?attr, "unknown attribute on enum constant") + } + } } } EntityKind::VisibilityAttr => {} @@ -1367,6 +1383,7 @@ impl Stmt { let result_type = Ty::parse_function_return(result_type, context); let mut arguments = Vec::new(); let mut must_use = false; + let mut link_name = None; if entity.is_static_method() { warn!("unexpected static method"); @@ -1402,10 +1419,20 @@ impl Stmt { EntityKind::WarnUnusedResultAttr => { must_use = true; } - EntityKind::PureAttr => { + EntityKind::PureAttr | EntityKind::ConstAttr => { // Ignore, we currently have no way of marking // external functions as pure in Rust. } + EntityKind::AsmLabelAttr => { + let name = entity.get_name().expect("asm label to have name"); + let name = if let Some(name) = name.strip_prefix('_') { + name.to_string() + } else { + error!(?name, "symbol did not start with _"); + name + }; + link_name = Some(name); + } EntityKind::VisibilityAttr => { // CG_EXTERN or UIKIT_EXTERN } @@ -1426,6 +1453,7 @@ impl Stmt { body, safe: !data.unsafe_, must_use, + link_name, }] } EntityKind::UnionDecl => { @@ -2360,6 +2388,7 @@ impl Stmt { body: Some(_), safe: _, must_use: _, + link_name: _, } => { write!(f, "// TODO: ")?; write!(f, "pub fn {}(", id.name)?; @@ -2377,6 +2406,7 @@ impl Stmt { body: None, safe: false, must_use, + link_name, } => { // Functions are always C-unwind, since we don't know // anything about them. @@ -2387,6 +2417,9 @@ impl Stmt { if *must_use { writeln!(f, " #[must_use]")?; } + if let Some(link_name) = link_name { + writeln!(f, " #[link_name = {link_name:?}]")?; + } write!(f, " pub fn {}(", id.name)?; for (param, arg_ty) in arguments { let param = handle_reserved(&crate::to_snake_case(param)); @@ -2405,6 +2438,7 @@ impl Stmt { body: None, safe: true, must_use, + link_name, } => { write!(f, "{}", self.cfg_gate_ln(config))?; write!(f, "{availability}")?; @@ -2421,6 +2455,9 @@ impl Stmt { writeln!(f, " extern \"C-unwind\" {{")?; + if let Some(link_name) = link_name { + writeln!(f, " #[link_name = {link_name:?}]")?; + } write!(f, " fn {}(", id.name)?; for (param, arg_ty) in arguments { let param = handle_reserved(&crate::to_snake_case(param)); diff --git a/crates/header-translator/src/unexposed_attr.rs b/crates/header-translator/src/unexposed_attr.rs index 53c5a76b4..4b86f9f49 100644 --- a/crates/header-translator/src/unexposed_attr.rs +++ b/crates/header-translator/src/unexposed_attr.rs @@ -37,11 +37,11 @@ impl UnexposedAttr { get_arguments: impl FnOnce() -> T, ) -> Result, ()> { Ok(match s { - "NS_ENUM" | "CF_ENUM" => { + "CF_ENUM" | "DISPATCH_ENUM" | "NS_ENUM" => { let _ = get_arguments(); Some(Self::Enum) } - "NS_OPTIONS" | "CF_OPTIONS" => { + "CF_OPTIONS" | "DISPATCH_OPTIONS" | "NS_OPTIONS" => { let _ = get_arguments(); Some(Self::Options) } @@ -84,6 +84,8 @@ impl UnexposedAttr { let _ = get_arguments(); None } + // Nullability attributes + s if s.starts_with("DISPATCH_NONNULL") => None, "NS_SWIFT_SENDABLE" | "AS_SWIFT_SENDABLE" | "CM_SWIFT_SENDABLE" | "CV_SWIFT_SENDABLE" => Some(Self::Sendable), "NS_SWIFT_NONSENDABLE" | "CM_SWIFT_NONSENDABLE" | "CV_SWIFT_NONSENDABLE" => { @@ -97,14 +99,14 @@ impl UnexposedAttr { let _ = get_arguments(); None } - "CF_NOESCAPE" | "NS_NOESCAPE" => Some(Self::NoEscape), + "CF_NOESCAPE" | "DISPATCH_NOESCAPE" | "NS_NOESCAPE" => Some(Self::NoEscape), // TODO: We could potentially automatically elide this argument // from the method call, though it's rare enough that it's // probably not really worth the effort. "__unused" => None, // We assume that a function is inline if it has a body, so not // interesting. - "CF_INLINE" | "NS_INLINE" => None, + "CF_INLINE" | "DISPATCH_ALWAYS_INLINE" | "NS_INLINE" => None, // We don't synthethize properties, so irrelevant for us. "NS_REQUIRES_PROPERTY_DEFINITIONS" => None, // Weak specifiers - would be interesting if Rust supported weak statics @@ -159,6 +161,8 @@ impl UnexposedAttr { | "CT_ENUM_UNAVAILABLE" | "CT_UNAVAILABLE" | "COREVIDEO_GL_DEPRECATED" + | "DISPATCH_SWIFT_UNAVAILABLE" + | "DISPATCH_ENUM_API_AVAILABLE" | "EVENTKITUI_CLASS_AVAILABLE" | "FPUI_AVAILABLE" | "MLCOMPUTE_AVAILABLE_STARTING" @@ -239,6 +243,7 @@ impl UnexposedAttr { | "CB_CM_API_AVAILABLE" | "deprecated" | "DEPRECATED_ATTRIBUTE" + | "DISPATCH_UNAVAILABLE" | "INTERAPP_AUDIO_DEPRECATED" | "MIDICI1_0" | "MIDICI1_1" @@ -279,9 +284,14 @@ impl UnexposedAttr { } "CF_IMPLICIT_BRIDGING_ENABLED" | "CF_REFINED_FOR_SWIFT" + | "DISPATCH_REFINED_FOR_SWIFT" | "NS_REFINED_FOR_SWIFT" | "NS_SWIFT_DISABLE_ASYNC" + | "DISPATCH_NOTHROW" | "NS_SWIFT_NOTHROW" => None, + // Possibly interesting? + "DISPATCH_COLD" => None, + "DISPATCH_MALLOC" => None, "CF_CONSUMED" | "CF_RELEASES_ARGUMENT" | "NS_RELEASES_ARGUMENT"