1
1
use std:: str:: FromStr ;
2
+ use std:: sync:: LazyLock ;
2
3
use std:: { fmt, iter} ;
3
4
4
5
use clang:: { CallingConvention , Entity , EntityKind , Nullability , Type , TypeKind } ;
@@ -7,6 +8,7 @@ use proc_macro2::{TokenStream, TokenTree};
7
8
use crate :: context:: Context ;
8
9
use crate :: display_helper:: FormatterFn ;
9
10
use crate :: id:: ItemIdentifier ;
11
+ use crate :: stmt:: is_bridged;
10
12
use crate :: stmt:: items_required_by_decl;
11
13
use crate :: thread_safety:: ThreadSafety ;
12
14
use crate :: unexposed_attr:: UnexposedAttr ;
@@ -444,6 +446,8 @@ pub enum Ty {
444
446
nullability : Nullability ,
445
447
lifetime : Lifetime ,
446
448
to : Box < Self > ,
449
+ /// Whether the typedef's declaration has a bridge attribute.
450
+ is_bridged : bool ,
447
451
} ,
448
452
IncompleteArray {
449
453
nullability : Nullability ,
@@ -464,6 +468,8 @@ pub enum Ty {
464
468
id : ItemIdentifier ,
465
469
/// FIXME: This does not work for recursive structs.
466
470
fields : Vec < Ty > ,
471
+ /// Whether the struct's declaration has a bridge attribute.
472
+ is_bridged : bool ,
467
473
} ,
468
474
Fn {
469
475
is_variadic : bool ,
@@ -614,6 +620,7 @@ impl Ty {
614
620
)
615
621
} )
616
622
. collect ( ) ,
623
+ is_bridged : is_bridged ( & declaration, context) ,
617
624
}
618
625
}
619
626
TypeKind :: Enum => {
@@ -1004,6 +1011,7 @@ impl Ty {
1004
1011
nullability,
1005
1012
lifetime,
1006
1013
to : Box :: new ( Self :: Primitive ( Primitive :: Int ) ) ,
1014
+ is_bridged : is_bridged ( & declaration, context) ,
1007
1015
}
1008
1016
}
1009
1017
@@ -1038,6 +1046,7 @@ impl Ty {
1038
1046
nullability,
1039
1047
lifetime,
1040
1048
to : Box :: new ( Self :: parse ( to, Lifetime :: Unspecified , context) ) ,
1049
+ is_bridged : is_bridged ( & declaration, context) ,
1041
1050
}
1042
1051
}
1043
1052
// Assume that functions without a prototype simply have 0 arguments.
@@ -1191,7 +1200,7 @@ impl Ty {
1191
1200
items. push ( id. clone ( ) ) ;
1192
1201
items
1193
1202
}
1194
- Self :: Struct { id, fields } => {
1203
+ Self :: Struct { id, fields, .. } => {
1195
1204
let mut items = Vec :: new ( ) ;
1196
1205
for field in fields {
1197
1206
items. extend ( field. required_items ( ) ) ;
@@ -1324,17 +1333,18 @@ impl Ty {
1324
1333
}
1325
1334
}
1326
1335
1327
- fn inner_typedef_is_object_life ( & self ) -> bool {
1336
+ fn inner_typedef_is_object_like ( & self , in_pointer : bool ) -> bool {
1328
1337
match self {
1329
1338
Self :: Class { .. }
1330
1339
| Self :: GenericParam { .. }
1331
1340
| Self :: AnyObject { .. }
1332
1341
| Self :: AnyProtocol
1333
1342
| Self :: AnyClass { .. }
1334
1343
| 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) ,
1338
1348
_ => false ,
1339
1349
}
1340
1350
}
@@ -1361,11 +1371,63 @@ impl Ty {
1361
1371
| Self :: AnyProtocol
1362
1372
| Self :: AnyClass { .. }
1363
1373
| 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 ( "\n CF_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
+ } ,
1365
1412
_ => false ,
1366
1413
}
1367
1414
}
1368
1415
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
+
1369
1431
pub ( crate ) fn is_objc_bool ( & self ) -> bool {
1370
1432
match self {
1371
1433
Self :: Primitive ( Primitive :: ObjcBool ) => true ,
@@ -1431,7 +1493,7 @@ impl Ty {
1431
1493
} ,
1432
1494
Self :: TypeDef {
1433
1495
id, nullability, ..
1434
- } if self . is_object_like ( ) => {
1496
+ } if self . is_object_like ( ) || self . is_cf_type ( ) => {
1435
1497
if * nullability == Nullability :: NonNull {
1436
1498
write ! ( f, "NonNull<{}>" , id. path( ) )
1437
1499
} else {
@@ -1491,7 +1553,9 @@ impl Ty {
1491
1553
Self :: Pointer { pointee, .. } if pointee. is_object_like ( ) => {
1492
1554
write ! ( f, "{}," , pointee. behind_pointer( ) ) ?
1493
1555
}
1494
- Self :: TypeDef { id, .. } if generic. is_object_like ( ) => {
1556
+ Self :: TypeDef { id, .. }
1557
+ if generic. is_object_like ( ) || generic. is_cf_type ( ) =>
1558
+ {
1495
1559
write ! ( f, "{}," , id. path( ) ) ?
1496
1560
}
1497
1561
generic => {
@@ -1572,7 +1636,9 @@ impl Ty {
1572
1636
}
1573
1637
Self :: TypeDef {
1574
1638
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.
1576
1642
if * nullability == Nullability :: NonNull {
1577
1643
write ! ( f, " -> Retained<{}>" , id. path( ) )
1578
1644
} else {
@@ -1624,7 +1690,8 @@ impl Ty {
1624
1690
nullability : Nullability :: Nullable ,
1625
1691
lifetime : Lifetime :: Unspecified ,
1626
1692
to : _,
1627
- } if self . is_object_like ( ) => {
1693
+ ..
1694
+ } if self . is_object_like ( ) || self . is_cf_type ( ) => {
1628
1695
// NULL -> error
1629
1696
write ! (
1630
1697
f,
@@ -1731,6 +1798,8 @@ impl Ty {
1731
1798
} ;
1732
1799
Some ( ( res, start, end ( * nullability) ) )
1733
1800
}
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 ,
1734
1803
_ => None ,
1735
1804
}
1736
1805
}
@@ -1753,7 +1822,7 @@ impl Ty {
1753
1822
}
1754
1823
Self :: TypeDef {
1755
1824
id, nullability, ..
1756
- } if self . is_object_like ( ) => {
1825
+ } if self . is_object_like ( ) || self . is_cf_type ( ) => {
1757
1826
if * nullability == Nullability :: NonNull {
1758
1827
write ! ( f, "&'static {}" , id. path( ) )
1759
1828
} else {
@@ -1774,9 +1843,10 @@ impl Ty {
1774
1843
} if pointee. is_object_like ( ) => {
1775
1844
write ! ( f, "{}" , pointee. behind_pointer( ) )
1776
1845
}
1777
- Self :: TypeDef { id, .. } if self . is_object_like ( ) => {
1846
+ Self :: TypeDef { id, .. } if self . is_object_like ( ) || self . is_cf_type ( ) => {
1778
1847
write ! ( f, "{}" , id. path( ) )
1779
1848
}
1849
+ Self :: IncompleteArray { .. } => unimplemented ! ( "incomplete array in typedef" ) ,
1780
1850
// Notice: We mark `typedefs` as-if behind a pointer
1781
1851
_ => write ! ( f, "{}" , self . behind_pointer( ) ) ,
1782
1852
} )
@@ -1798,7 +1868,7 @@ impl Ty {
1798
1868
}
1799
1869
Self :: TypeDef {
1800
1870
id, nullability, ..
1801
- } if self . is_object_like ( ) => {
1871
+ } if self . is_object_like ( ) || self . is_cf_type ( ) => {
1802
1872
if * nullability == Nullability :: NonNull {
1803
1873
write ! ( f, "&{}" , id. path( ) )
1804
1874
} else {
@@ -2035,30 +2105,37 @@ impl Ty {
2035
2105
Self :: parse_method_return ( ty, false , context)
2036
2106
}
2037
2107
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
+ }
2044
2111
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
+ }
2061
2137
}
2138
+ None
2062
2139
}
2063
2140
2064
2141
pub ( crate ) fn parse_property (
@@ -2190,7 +2267,11 @@ impl Ty {
2190
2267
{
2191
2268
true
2192
2269
}
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
+ }
2194
2275
_ => false ,
2195
2276
}
2196
2277
}
@@ -2344,7 +2425,9 @@ mod tests {
2344
2425
protocols : vec ! [ ] ,
2345
2426
} ) ,
2346
2427
} ) ,
2428
+ is_bridged : false ,
2347
2429
} ) ,
2430
+ is_bridged : false ,
2348
2431
} ;
2349
2432
2350
2433
assert ! ( ty. is_object_like( ) ) ;
0 commit comments