Skip to content

Commit c4bfe9d

Browse files
committed
Redo how ownership/mutability works
Add associated type `ClassType::Mutability`, which describes the mutability of an object. This replaces the `Ownership` parameter in `Id<T, O>` (so we're moving the mutability/ownership specification from use site to declaration site, which is a bit more restrictive, but much more succinct, as well as being easier to understand and harder to make mistakes). Along with this, we can also finally fix `NSCopying` and `NSMutableCopying` in a way that `ProtocolObject<dyn NSCopying>` is allowed.
1 parent 72288fb commit c4bfe9d

File tree

178 files changed

+4233
-3114
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

178 files changed

+4233
-3114
lines changed

Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

LAYERED_SAFETY.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ correctly.
144144
The `NSData` example again.
145145

146146
```rust
147-
let obj: Id<NSObject, Shared> = unsafe { msg_send_id![class!(NSData), new] };
147+
let obj: Id<NSObject> = unsafe { msg_send_id![class!(NSData), new] };
148148
let length: NSUInteger = unsafe { msg_send![&obj, length] };
149149
// `obj` goes out of scope, `release` is automatically sent to the object
150150
```
@@ -181,13 +181,14 @@ extern_class!(
181181

182182
unsafe impl ClassType for NSData {
183183
type Super = NSObject;
184+
type Mutability = ImmutableWithMutableSubclass<NSMutableData>;
184185
}
185186
);
186187

187188
extern_methods!(
188189
unsafe impl NSData {
189190
#[method_id(new)]
190-
fn new() -> Id<Self, Shared>;
191+
fn new() -> Id<Self>;
191192

192193
#[method(length)]
193194
fn length(&self) -> NSUInteger;

crates/header-translator/src/cache.rs

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use crate::file::File;
77
use crate::id::ItemIdentifier;
88
use crate::method::Method;
99
use crate::output::Output;
10-
use crate::rust_type::Ownership;
1110
use crate::stmt::Stmt;
1211

1312
#[derive(Debug, PartialEq, Clone)]
@@ -37,14 +36,12 @@ impl ClassCache {
3736
#[derive(Debug, PartialEq, Clone)]
3837
pub struct Cache<'a> {
3938
classes: BTreeMap<ItemIdentifier, ClassCache>,
40-
ownership_map: BTreeMap<String, Ownership>,
4139
config: &'a Config,
4240
}
4341

4442
impl<'a> Cache<'a> {
4543
pub fn new(output: &Output, config: &'a Config) -> Self {
4644
let mut classes: BTreeMap<_, ClassCache> = BTreeMap::new();
47-
let mut ownership_map: BTreeMap<_, Ownership> = BTreeMap::new();
4845

4946
for (name, library) in &output.libraries {
5047
let _span = debug_span!("library", name).entered();
@@ -55,20 +52,11 @@ impl<'a> Cache<'a> {
5552
let cache = classes.entry(cls.clone()).or_default();
5653
cache.to_emit.push(method_cache);
5754
}
58-
if let Stmt::ClassDecl { id, ownership, .. } = stmt {
59-
if *ownership != Ownership::default() {
60-
ownership_map.insert(id.name.clone(), ownership.clone());
61-
}
62-
}
6355
}
6456
}
6557
}
6658

67-
Self {
68-
classes,
69-
ownership_map,
70-
config,
71-
}
59+
Self { classes, config }
7260
}
7361

7462
fn cache_stmt(stmt: &Stmt) -> Option<(&ItemIdentifier, MethodCache)> {
@@ -159,6 +147,7 @@ impl<'a> Cache<'a> {
159147

160148
let mut new_stmts = Vec::new();
161149
for stmt in &mut file.stmts {
150+
#[allow(clippy::single_match)] // There will be others
162151
match stmt {
163152
Stmt::ClassDecl {
164153
id,
@@ -182,7 +171,7 @@ impl<'a> Cache<'a> {
182171
for (superclass, _) in &*superclasses {
183172
if let Some(cache) = self.classes.get(superclass) {
184173
new_stmts.extend(cache.to_emit.iter().filter_map(|cache| {
185-
let mut methods: Vec<_> = cache
174+
let methods: Vec<_> = cache
186175
.methods
187176
.iter()
188177
.filter(|method| !seen_methods.contains(&method.id()))
@@ -197,8 +186,6 @@ impl<'a> Cache<'a> {
197186
return None;
198187
}
199188

200-
self.update_methods(&mut methods, &id.name);
201-
202189
Some(Stmt::Methods {
203190
cls: id.clone(),
204191
generics: generics.clone(),
@@ -217,12 +204,6 @@ impl<'a> Cache<'a> {
217204
}
218205
}
219206
}
220-
Stmt::Methods { cls, methods, .. } => {
221-
self.update_methods(methods, &cls.name);
222-
}
223-
Stmt::ProtocolDecl { id, methods, .. } => {
224-
self.update_methods(methods, &id.name);
225-
}
226207
_ => {}
227208
}
228209
}
@@ -257,17 +238,4 @@ impl<'a> Cache<'a> {
257238
file.stmts.push(stmt);
258239
}
259240
}
260-
261-
fn update_methods(&self, methods: &mut [Method], self_means: &str) {
262-
for method in methods {
263-
// Beware! We make instance methods return `Owned` as well, though
264-
// those are basically never safe (since they'd refer to mutable
265-
// data without a lifetime tied to the primary owner).
266-
method.result_type.set_ownership(|name| {
267-
let name = if name == "Self" { self_means } else { name };
268-
269-
self.ownership_map.get(name).cloned().unwrap_or_default()
270-
});
271-
}
272-
}
273241
}

crates/header-translator/src/config.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use std::path::Path;
66
use serde::Deserialize;
77

88
use crate::data;
9-
use crate::rust_type::Ownership;
10-
use crate::stmt::Derives;
9+
use crate::stmt::{Derives, Mutability};
1110

1211
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
1312
#[serde(deny_unknown_fields)]
@@ -96,9 +95,8 @@ pub struct ClassData {
9695
pub categories: HashMap<String, CategoryData>,
9796
#[serde(default)]
9897
pub derives: Derives,
99-
#[serde(rename = "owned")]
100-
#[serde(default)]
101-
pub ownership: Ownership,
98+
#[serde(skip)]
99+
pub mutability: Mutability,
102100
#[serde(rename = "skipped-protocols")]
103101
#[serde(default)]
104102
pub skipped_protocols: HashSet<String>,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
data! {
2+
// Subclasses `NSMutableAttributedString`, though I think this should
3+
// actually be `InteriorMutable`?
4+
class NSTextStorage: Mutable {}
25
}

crates/header-translator/src/data/Foundation.rs

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
data! {
2-
class NSArray {
2+
// SAFETY: `new` or `initWithObjects:` may choose to deduplicate arrays,
3+
// and returning mutable references to those would be unsound - hence
4+
// `NSArray` cannot be mutable.
5+
class NSArray: ImmutableWithMutableSubclass<Foundation::NSMutableArray> {
36
unsafe -count;
47
}
58

6-
class NSMutableArray: Owned {
9+
class NSMutableArray: MutableWithImmutableSuperclass<Foundation::NSArray> {
710
unsafe mut -removeAllObjects;
811
mut -addObject;
912
mut -insertObject_atIndex;
@@ -13,7 +16,7 @@ data! {
1316
mut -sortUsingFunction_context;
1417
}
1518

16-
class NSString {
19+
class NSString: ImmutableWithMutableSubclass<Foundation::NSMutableString> {
1720
unsafe -init;
1821
unsafe -compare;
1922
unsafe -hasPrefix;
@@ -30,7 +33,7 @@ data! {
3033
unsafe +stringWithString;
3134
}
3235

33-
class NSMutableString: Owned {
36+
class NSMutableString: MutableWithImmutableSuperclass<Foundation::NSString> {
3437
unsafe -initWithCapacity;
3538
unsafe +stringWithCapacity;
3639
unsafe -initWithString;
@@ -39,14 +42,20 @@ data! {
3942
unsafe mut -setString;
4043
}
4144

42-
class NSAttributedString {
45+
// Allowed to be just `Immutable` since we've removed the `NSCopying` and
46+
// `NSMutableCopying` impls from these for now (they'd return the wrong
47+
// type).
48+
class NSSimpleCString: Immutable {}
49+
class NSConstantString: Immutable {}
50+
51+
class NSAttributedString: ImmutableWithMutableSubclass<Foundation::NSMutableAttributedString> {
4352
unsafe -initWithString;
4453
unsafe -initWithAttributedString;
4554
unsafe -string;
4655
unsafe -length;
4756
}
4857

49-
class NSMutableAttributedString: Owned {
58+
class NSMutableAttributedString: MutableWithImmutableSuperclass<Foundation::NSAttributedString> {
5059
unsafe -initWithString;
5160
unsafe -initWithAttributedString;
5261
unsafe mut -setAttributedString;
@@ -57,32 +66,34 @@ data! {
5766
unsafe -infoDictionary;
5867
}
5968

60-
class NSData {
69+
class NSData: ImmutableWithMutableSubclass<Foundation::NSMutableData> {
6170
unsafe -initWithData;
6271
unsafe +dataWithData;
6372
unsafe -length;
6473
unsafe -bytes;
6574
}
6675

67-
class NSMutableData: Owned {
76+
class NSMutableData: MutableWithImmutableSuperclass<Foundation::NSData> {
6877
unsafe +dataWithData;
6978
unsafe -initWithCapacity;
7079
unsafe +dataWithCapacity;
7180
unsafe mut -setLength;
7281
unsafe mut -mutableBytes;
7382
}
7483

75-
class NSDictionary {
84+
// Allowed to be just `Mutable` since we've removed the `NSCopying` and
85+
// `NSMutableCopying` impls from this for now (since they'd return the
86+
// wrong type).
87+
class NSPurgeableData: Mutable {}
88+
89+
class NSDictionary: ImmutableWithMutableSubclass<Foundation::NSMutableDictionary> {
7690
unsafe -count;
77-
unsafe -objectForKey;
78-
unsafe -allKeys;
79-
unsafe -allValues;
8091
}
8192

82-
class NSMutableDictionary: Owned {
83-
unsafe mut -setDictionary;
93+
class NSMutableDictionary: MutableWithImmutableSuperclass<Foundation::NSDictionary> {
8494
unsafe mut -removeObjectForKey;
8595
unsafe mut -removeAllObjects;
96+
mut -setDictionary;
8697
}
8798

8899
class NSError {
@@ -130,20 +141,23 @@ data! {
130141
unsafe -operatingSystemVersion;
131142
}
132143

133-
class NSSet {
144+
class NSSet: ImmutableWithMutableSubclass<Foundation::NSMutableSet> {
134145
unsafe -count;
135146
}
136147

137-
class NSMutableSet: Owned {
148+
class NSMutableSet: MutableWithImmutableSuperclass<Foundation::NSSet> {
138149
unsafe mut -removeAllObjects;
139150
mut -addObject;
140151
}
141152

142-
class NSMutableCharacterSet: Owned {}
153+
class NSCharacterSet: ImmutableWithMutableSubclass<Foundation::NSMutableCharacterSet> {}
154+
class NSMutableCharacterSet: MutableWithImmutableSuperclass<Foundation::NSCharacterSet> {}
143155

144-
class NSMutableOrderedSet: Owned {}
156+
class NSOrderedSet: ImmutableWithMutableSubclass<Foundation::NSMutableOrderedSet> {}
157+
class NSMutableOrderedSet: MutableWithImmutableSuperclass<Foundation::NSOrderedSet> {}
145158

146-
class NSMutableIndexSet: Owned {}
159+
class NSIndexSet: ImmutableWithMutableSubclass<Foundation::NSMutableIndexSet> {}
160+
class NSMutableIndexSet: MutableWithImmutableSuperclass<Foundation::NSIndexSet> {}
147161

148162
class NSNumber {
149163
unsafe -initWithChar;
@@ -196,5 +210,6 @@ data! {
196210
unsafe -stringValue;
197211
}
198212

199-
class NSMutableURLRequest: Owned {}
213+
class NSURLRequest: ImmutableWithMutableSubclass<Foundation::NSMutableURLRequest> {}
214+
class NSMutableURLRequest: MutableWithImmutableSuperclass<Foundation::NSURLRequest> {}
200215
}

crates/header-translator/src/data/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,16 @@ marked safe, so such an improvement can be made in a minor version!
4141

4242
```rust , ignore
4343
data! {
44-
// Everywhere that the class is returned, it is as
45-
// `Id<MyMutableClass, Owned>`.
46-
//
47-
// Specifying this is _not_ unsafe, but every method that returns this
48-
// class must be checked for safety with it in mind.
49-
class MyMutableClass: Owned {
44+
class MyClass {
5045
// The class method `new` is safe.
5146
unsafe +new;
5247
// The method `init` is safe.
5348
unsafe -init;
49+
}
50+
51+
class MyImmutableClass: ImmutableWithMutableSubclass<MyMutableClass> {}
52+
53+
class MyMutableClass: MutableWithImmutableSuperclass<MyImmutableClass> {
5454
// The method `mutate` takes &mut self, and is safe.
5555
unsafe mut -mutate;
5656
// The method `mutateUnchecked` takes &mut self, but is not safe.

crates/header-translator/src/data/macros.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,32 @@ macro_rules! data {
4545
};
4646
}
4747

48+
macro_rules! __set_mutability {
49+
($data:expr;) => {};
50+
($data:expr; ImmutableWithMutableSubclass<$framework:ident::$subclass:ident>) => {
51+
$data.mutability = $crate::stmt::Mutability::ImmutableWithMutableSubclass(
52+
$crate::ItemIdentifier::from_raw(
53+
stringify!($subclass).to_string(),
54+
stringify!($framework).to_string(),
55+
),
56+
);
57+
};
58+
($data:expr; MutableWithImmutableSuperclass<$framework:ident::$superclass:ident>) => {
59+
$data.mutability = $crate::stmt::Mutability::MutableWithImmutableSuperclass(
60+
$crate::ItemIdentifier::from_raw(
61+
stringify!($superclass).to_string(),
62+
stringify!($framework).to_string(),
63+
),
64+
);
65+
};
66+
($data:expr; Immutable) => {
67+
$data.mutability = $crate::stmt::Mutability::Immutable;
68+
};
69+
($data:expr; Mutable) => {
70+
$data.mutability = $crate::stmt::Mutability::Mutable;
71+
};
72+
}
73+
4874
macro_rules! __data_inner {
4975
// Base case
5076
(
@@ -54,7 +80,7 @@ macro_rules! __data_inner {
5480
(
5581
@($config:expr)
5682

57-
class $class:ident $(: $ownership:ident)? {
83+
class $class:ident $(: $mutability:ident $(<$framework:ident::$ty:ident>)?)? {
5884
$($methods:tt)*
5985
}
6086

@@ -63,7 +89,10 @@ macro_rules! __data_inner {
6389
#[allow(unused_mut)]
6490
let mut data = $config.class_data.entry(stringify!($class).to_string()).or_default();
6591

66-
$(data.ownership = $crate::rust_type::Ownership::$ownership;)?
92+
__set_mutability! {
93+
data;
94+
$($mutability $(<$framework::$ty>)?)?
95+
}
6796

6897
__data_methods! {
6998
@(data)

0 commit comments

Comments
 (0)