Skip to content

Commit 10c717a

Browse files
committed
Emit opaque structs
Emit the opaque inner struct in typedefs like AudioConverterRef. This avoids the need for the `"relax-void-encoding"` feature. Furthermore, add partial support for CoreFoundation-like types. This includes automatic retain/release management (when they're use in methods, doing this in functions is not yet supported). Big part of #556.
1 parent 9bee7df commit 10c717a

File tree

14 files changed

+1120
-64
lines changed

14 files changed

+1120
-64
lines changed

crates/header-translator/src/CFDatabase.def

Lines changed: 590 additions & 0 deletions
Large diffs are not rendered by default.

crates/header-translator/src/id.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,13 @@ impl ItemIdentifier {
402402
}
403403
}
404404

405+
pub fn cf(name: impl Into<String>) -> Self {
406+
Self {
407+
name: name.into(),
408+
location: Location::new("CoreFoundation"),
409+
}
410+
}
411+
405412
pub fn core_ffi(name: &str) -> Self {
406413
Self {
407414
name: name.into(),
@@ -416,6 +423,20 @@ impl ItemIdentifier {
416423
}
417424
}
418425

426+
pub fn unsafecell() -> Self {
427+
Self {
428+
name: "UnsafeCell".into(),
429+
location: Location::new("__core__.cell"),
430+
}
431+
}
432+
433+
pub fn phantoms() -> Self {
434+
Self {
435+
name: "__phantoms__".into(),
436+
location: Location::new("__core__.marker"),
437+
}
438+
}
439+
419440
pub fn main_thread_marker() -> Self {
420441
Self {
421442
name: "MainThreadMarker".into(),
@@ -445,7 +466,12 @@ impl ItemIdentifier {
445466
"__builtin__" => None,
446467
"__core__" => match &*self.location().module_path {
447468
"__core__.ffi" => Some("core::ffi::*".into()),
469+
// HACKs
448470
"__core__.ptr" if self.name == "NonNull" => Some("core::ptr::NonNull".into()),
471+
"__core__.cell" if self.name == "UnsafeCell" => {
472+
Some("core::cell::UnsafeCell".into())
473+
}
474+
"__core__.marker" => Some("core::marker::{PhantomData, PhantomPinned}".into()),
449475
_ => {
450476
error!("unknown __core__: {self:?}");
451477
None

crates/header-translator/src/main.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -744,10 +744,7 @@ fn update_test_metadata<'a>(
744744
// FIXME: Make these not required for tests
745745
(
746746
"features",
747-
toml_edit::Value::Array(toml_edit::Array::from_iter([
748-
"relax-void-encoding",
749-
"relax-sign-encoding",
750-
])),
747+
toml_edit::Value::Array(toml_edit::Array::from_iter(["relax-sign-encoding"])),
751748
),
752749
])),
753750
),

crates/header-translator/src/rust_type.rs

Lines changed: 119 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::str::FromStr;
2+
use std::sync::LazyLock;
23
use std::{fmt, iter};
34

45
use clang::{CallingConvention, Entity, EntityKind, Nullability, Type, TypeKind};
@@ -7,6 +8,7 @@ use proc_macro2::{TokenStream, TokenTree};
78
use crate::context::Context;
89
use crate::display_helper::FormatterFn;
910
use crate::id::ItemIdentifier;
11+
use crate::stmt::is_bridged;
1012
use crate::stmt::items_required_by_decl;
1113
use crate::thread_safety::ThreadSafety;
1214
use crate::unexposed_attr::UnexposedAttr;
@@ -444,6 +446,8 @@ pub enum Ty {
444446
nullability: Nullability,
445447
lifetime: Lifetime,
446448
to: Box<Self>,
449+
/// Whether the typedef's declaration has a bridge attribute.
450+
is_bridged: bool,
447451
},
448452
IncompleteArray {
449453
nullability: Nullability,
@@ -464,6 +468,8 @@ pub enum Ty {
464468
id: ItemIdentifier,
465469
/// FIXME: This does not work for recursive structs.
466470
fields: Vec<Ty>,
471+
/// Whether the struct's declaration has a bridge attribute.
472+
is_bridged: bool,
467473
},
468474
Fn {
469475
is_variadic: bool,
@@ -614,6 +620,7 @@ impl Ty {
614620
)
615621
})
616622
.collect(),
623+
is_bridged: is_bridged(&declaration, context),
617624
}
618625
}
619626
TypeKind::Enum => {
@@ -1004,6 +1011,7 @@ impl Ty {
10041011
nullability,
10051012
lifetime,
10061013
to: Box::new(Self::Primitive(Primitive::Int)),
1014+
is_bridged: is_bridged(&declaration, context),
10071015
}
10081016
}
10091017

@@ -1038,6 +1046,7 @@ impl Ty {
10381046
nullability,
10391047
lifetime,
10401048
to: Box::new(Self::parse(to, Lifetime::Unspecified, context)),
1049+
is_bridged: is_bridged(&declaration, context),
10411050
}
10421051
}
10431052
// Assume that functions without a prototype simply have 0 arguments.
@@ -1191,7 +1200,7 @@ impl Ty {
11911200
items.push(id.clone());
11921201
items
11931202
}
1194-
Self::Struct { id, fields } => {
1203+
Self::Struct { id, fields, .. } => {
11951204
let mut items = Vec::new();
11961205
for field in fields {
11971206
items.extend(field.required_items());
@@ -1324,17 +1333,18 @@ impl Ty {
13241333
}
13251334
}
13261335

1327-
fn inner_typedef_is_object_life(&self) -> bool {
1336+
fn inner_typedef_is_object_like(&self, in_pointer: bool) -> bool {
13281337
match self {
13291338
Self::Class { .. }
13301339
| Self::GenericParam { .. }
13311340
| Self::AnyObject { .. }
13321341
| Self::AnyProtocol
13331342
| Self::AnyClass { .. }
13341343
| Self::Self_ => true,
1335-
// FIXME: This recurses badly and too deeply
1336-
Self::Pointer { pointee, .. } => pointee.inner_typedef_is_object_life(),
1337-
Self::TypeDef { to, .. } => to.inner_typedef_is_object_life(),
1344+
Self::Pointer { pointee, .. } if !in_pointer => {
1345+
pointee.inner_typedef_is_object_like(true)
1346+
}
1347+
Self::TypeDef { to, .. } => to.inner_typedef_is_object_like(in_pointer),
13381348
_ => false,
13391349
}
13401350
}
@@ -1361,11 +1371,63 @@ impl Ty {
13611371
| Self::AnyProtocol
13621372
| Self::AnyClass { .. }
13631373
| Self::Self_ => true,
1364-
Self::TypeDef { to, .. } => to.inner_typedef_is_object_life(),
1374+
Self::TypeDef { to, .. } => to.inner_typedef_is_object_like(false),
1375+
_ => false,
1376+
}
1377+
}
1378+
1379+
pub(crate) fn is_inner_cf_type(&self, typedef_name: &str, typedef_is_bridged: bool) -> bool {
1380+
// Pre-defined list of known CF types.
1381+
// Taken from the Swift project (i.e. this is also what they do).
1382+
static KNOWN_CF_TYPES: LazyLock<Vec<&'static str>> = LazyLock::new(|| {
1383+
let database = include_str!("CFDatabase.def");
1384+
let mut res = vec![];
1385+
for item in database.split("\nCF_TYPE(").skip(1) {
1386+
let (typename, _) = item.split_once(")").unwrap();
1387+
res.push(typename);
1388+
}
1389+
res
1390+
});
1391+
1392+
// TODO: Figure out when to do the isCFObjectRef check that Clang does:
1393+
// <https://github.com/llvm/llvm-project/blob/llvmorg-19.1.6/clang/lib/Analysis/CocoaConventions.cpp#L57>
1394+
match self {
1395+
// Recurse
1396+
Self::TypeDef { .. } => self.is_cf_type(),
1397+
Self::Pointer { pointee, .. } => match &**pointee {
1398+
// Typedefs to structs are CF types if bridged, or in
1399+
// pre-defined list.
1400+
Self::Struct { is_bridged, .. } => {
1401+
*is_bridged || KNOWN_CF_TYPES.contains(&typedef_name)
1402+
}
1403+
// Typedefs to void* are CF types if the typedef is
1404+
// bridged, or in pre-defined list.
1405+
Self::Primitive(Primitive::Void) => {
1406+
// TODO
1407+
let enabled = false;
1408+
enabled && (typedef_is_bridged || KNOWN_CF_TYPES.contains(&typedef_name))
1409+
}
1410+
_ => false,
1411+
},
13651412
_ => false,
13661413
}
13671414
}
13681415

1416+
/// Determine whether the typedef is a CF-like type.
1417+
///
1418+
/// Similar to what's done in Swift's implementation:
1419+
/// <https://github.com/swiftlang/swift/blob/swift-6.0.3-RELEASE/lib/ClangImporter/CFTypeInfo.cpp#L53>
1420+
fn is_cf_type(&self) -> bool {
1421+
if let Self::TypeDef {
1422+
id, to, is_bridged, ..
1423+
} = self
1424+
{
1425+
to.is_inner_cf_type(&id.name, *is_bridged)
1426+
} else {
1427+
false
1428+
}
1429+
}
1430+
13691431
pub(crate) fn is_objc_bool(&self) -> bool {
13701432
match self {
13711433
Self::Primitive(Primitive::ObjcBool) => true,
@@ -1431,7 +1493,7 @@ impl Ty {
14311493
},
14321494
Self::TypeDef {
14331495
id, nullability, ..
1434-
} if self.is_object_like() => {
1496+
} if self.is_object_like() || self.is_cf_type() => {
14351497
if *nullability == Nullability::NonNull {
14361498
write!(f, "NonNull<{}>", id.path())
14371499
} else {
@@ -1491,7 +1553,9 @@ impl Ty {
14911553
Self::Pointer { pointee, .. } if pointee.is_object_like() => {
14921554
write!(f, "{},", pointee.behind_pointer())?
14931555
}
1494-
Self::TypeDef { id, .. } if generic.is_object_like() => {
1556+
Self::TypeDef { id, .. }
1557+
if generic.is_object_like() || generic.is_cf_type() =>
1558+
{
14951559
write!(f, "{},", id.path())?
14961560
}
14971561
generic => {
@@ -1572,7 +1636,9 @@ impl Ty {
15721636
}
15731637
Self::TypeDef {
15741638
id, nullability, ..
1575-
} if self.is_object_like() && !self.is_static_object() => {
1639+
} if (self.is_object_like() || self.is_cf_type()) && !self.is_static_object() => {
1640+
// NOTE: We return CF types as `Retained` for now, since we
1641+
// don't have support for the CF wrapper in msg_send! yet.
15761642
if *nullability == Nullability::NonNull {
15771643
write!(f, " -> Retained<{}>", id.path())
15781644
} else {
@@ -1624,7 +1690,8 @@ impl Ty {
16241690
nullability: Nullability::Nullable,
16251691
lifetime: Lifetime::Unspecified,
16261692
to: _,
1627-
} if self.is_object_like() => {
1693+
..
1694+
} if self.is_object_like() || self.is_cf_type() => {
16281695
// NULL -> error
16291696
write!(
16301697
f,
@@ -1731,6 +1798,8 @@ impl Ty {
17311798
};
17321799
Some((res, start, end(*nullability)))
17331800
}
1801+
// TODO: Use custom CF type to do retain/release management here.
1802+
Self::TypeDef { .. } if self.is_cf_type() && !self.is_static_object() => None,
17341803
_ => None,
17351804
}
17361805
}
@@ -1753,7 +1822,7 @@ impl Ty {
17531822
}
17541823
Self::TypeDef {
17551824
id, nullability, ..
1756-
} if self.is_object_like() => {
1825+
} if self.is_object_like() || self.is_cf_type() => {
17571826
if *nullability == Nullability::NonNull {
17581827
write!(f, "&'static {}", id.path())
17591828
} else {
@@ -1774,9 +1843,10 @@ impl Ty {
17741843
} if pointee.is_object_like() => {
17751844
write!(f, "{}", pointee.behind_pointer())
17761845
}
1777-
Self::TypeDef { id, .. } if self.is_object_like() => {
1846+
Self::TypeDef { id, .. } if self.is_object_like() || self.is_cf_type() => {
17781847
write!(f, "{}", id.path())
17791848
}
1849+
Self::IncompleteArray { .. } => unimplemented!("incomplete array in typedef"),
17801850
// Notice: We mark `typedefs` as-if behind a pointer
17811851
_ => write!(f, "{}", self.behind_pointer()),
17821852
})
@@ -1798,7 +1868,7 @@ impl Ty {
17981868
}
17991869
Self::TypeDef {
18001870
id, nullability, ..
1801-
} if self.is_object_like() => {
1871+
} if self.is_object_like() || self.is_cf_type() => {
18021872
if *nullability == Nullability::NonNull {
18031873
write!(f, "&{}", id.path())
18041874
} else {
@@ -2035,30 +2105,37 @@ impl Ty {
20352105
Self::parse_method_return(ty, false, context)
20362106
}
20372107

2038-
pub(crate) fn parse_typedef(
2039-
ty: Type<'_>,
2040-
typedef_name: &str,
2041-
context: &Context<'_>,
2042-
) -> Option<Self> {
2043-
let mut ty = Self::parse(ty, Lifetime::Unspecified, context);
2108+
pub(crate) fn parse_typedef(ty: Type<'_>, context: &Context<'_>) -> Self {
2109+
Self::parse(ty, Lifetime::Unspecified, context)
2110+
}
20442111

2045-
match &mut ty {
2046-
// Handled by Stmt::EnumDecl
2047-
Self::Enum { .. } => None,
2048-
// No need to output a typedef if it'll just point to the same thing.
2049-
//
2050-
// TODO: We're discarding a slight bit of availability data this way.
2051-
Self::Struct { id, .. } if id.name == typedef_name => None,
2052-
// Opaque structs
2053-
Self::Pointer { pointee, .. } if matches!(&**pointee, Self::Struct { .. }) => {
2054-
**pointee = Self::Primitive(Primitive::Void);
2055-
Some(ty)
2056-
}
2057-
Self::IncompleteArray { .. } => {
2058-
unimplemented!("incomplete array in struct")
2059-
}
2060-
_ => Some(ty),
2112+
pub(crate) fn is_enum(&self) -> bool {
2113+
matches!(self, Self::Enum { .. })
2114+
}
2115+
2116+
pub(crate) fn pointer_to_opaque_struct(&self) -> Option<&str> {
2117+
if let Self::Pointer {
2118+
pointee,
2119+
is_const: _, // const-ness doesn't matter when defining the type
2120+
nullability,
2121+
lifetime,
2122+
} = self
2123+
{
2124+
if let Self::Struct { id, fields, .. } = &**pointee {
2125+
if fields.is_empty() {
2126+
// Extra checks to ensure we don't loose information
2127+
if *nullability != Nullability::Unspecified {
2128+
error!(?id, ?nullability, "opaque pointer had nullability");
2129+
}
2130+
if *lifetime != Lifetime::Unspecified {
2131+
error!(?id, ?lifetime, "opaque pointer had lifetime");
2132+
}
2133+
2134+
return Some(&id.name);
2135+
}
2136+
}
20612137
}
2138+
None
20622139
}
20632140

20642141
pub(crate) fn parse_property(
@@ -2190,7 +2267,11 @@ impl Ty {
21902267
{
21912268
true
21922269
}
2193-
Self::TypeDef { .. } if self.is_object_like() && !self.is_static_object() => true,
2270+
Self::TypeDef { .. }
2271+
if self.is_object_like() || self.is_cf_type() && !self.is_static_object() =>
2272+
{
2273+
true
2274+
}
21942275
_ => false,
21952276
}
21962277
}
@@ -2344,7 +2425,9 @@ mod tests {
23442425
protocols: vec![],
23452426
}),
23462427
}),
2428+
is_bridged: false,
23472429
}),
2430+
is_bridged: false,
23482431
};
23492432

23502433
assert!(ty.is_object_like());

0 commit comments

Comments
 (0)