Skip to content

Commit

Permalink
Merge pull request #419 from madsmtm/redo-ownership
Browse files Browse the repository at this point in the history
Redo how ownership works
  • Loading branch information
madsmtm authored Apr 21, 2023
2 parents 72288fb + 01f08a7 commit 032809f
Show file tree
Hide file tree
Showing 189 changed files with 4,890 additions and 3,194 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ jobs:
key: cargo-${{ github.job }}-${{ matrix.name }}-${{ hashFiles('**/Cargo.lock') }}

- name: cargo doc
# Disable cargo doc checking for now, `NSEnumerator2<'a, T>` is broken
# on current nightly.
if: false
run: cargo doc --no-deps --document-private-items ${{ matrix.args }}

- name: cargo clippy
Expand Down
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions LAYERED_SAFETY.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ correctly.
The `NSData` example again.

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

unsafe impl ClassType for NSData {
type Super = NSObject;
type Mutability = ImmutableWithMutableSubclass<NSMutableData>;
}
);

extern_methods!(
unsafe impl NSData {
#[method_id(new)]
fn new() -> Id<Self, Shared>;
fn new() -> Id<Self>;

#[method(length)]
fn length(&self) -> NSUInteger;
Expand Down
38 changes: 3 additions & 35 deletions crates/header-translator/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use crate::file::File;
use crate::id::ItemIdentifier;
use crate::method::Method;
use crate::output::Output;
use crate::rust_type::Ownership;
use crate::stmt::Stmt;

#[derive(Debug, PartialEq, Clone)]
Expand Down Expand Up @@ -37,14 +36,12 @@ impl ClassCache {
#[derive(Debug, PartialEq, Clone)]
pub struct Cache<'a> {
classes: BTreeMap<ItemIdentifier, ClassCache>,
ownership_map: BTreeMap<String, Ownership>,
config: &'a Config,
}

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

for (name, library) in &output.libraries {
let _span = debug_span!("library", name).entered();
Expand All @@ -55,20 +52,11 @@ impl<'a> Cache<'a> {
let cache = classes.entry(cls.clone()).or_default();
cache.to_emit.push(method_cache);
}
if let Stmt::ClassDecl { id, ownership, .. } = stmt {
if *ownership != Ownership::default() {
ownership_map.insert(id.name.clone(), ownership.clone());
}
}
}
}
}

Self {
classes,
ownership_map,
config,
}
Self { classes, config }
}

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

let mut new_stmts = Vec::new();
for stmt in &mut file.stmts {
#[allow(clippy::single_match)] // There will be others
match stmt {
Stmt::ClassDecl {
id,
Expand All @@ -182,7 +171,7 @@ impl<'a> Cache<'a> {
for (superclass, _) in &*superclasses {
if let Some(cache) = self.classes.get(superclass) {
new_stmts.extend(cache.to_emit.iter().filter_map(|cache| {
let mut methods: Vec<_> = cache
let methods: Vec<_> = cache
.methods
.iter()
.filter(|method| !seen_methods.contains(&method.id()))
Expand All @@ -197,8 +186,6 @@ impl<'a> Cache<'a> {
return None;
}

self.update_methods(&mut methods, &id.name);

Some(Stmt::Methods {
cls: id.clone(),
generics: generics.clone(),
Expand All @@ -217,12 +204,6 @@ impl<'a> Cache<'a> {
}
}
}
Stmt::Methods { cls, methods, .. } => {
self.update_methods(methods, &cls.name);
}
Stmt::ProtocolDecl { id, methods, .. } => {
self.update_methods(methods, &id.name);
}
_ => {}
}
}
Expand Down Expand Up @@ -257,17 +238,4 @@ impl<'a> Cache<'a> {
file.stmts.push(stmt);
}
}

fn update_methods(&self, methods: &mut [Method], self_means: &str) {
for method in methods {
// Beware! We make instance methods return `Owned` as well, though
// those are basically never safe (since they'd refer to mutable
// data without a lifetime tied to the primary owner).
method.result_type.set_ownership(|name| {
let name = if name == "Self" { self_means } else { name };

self.ownership_map.get(name).cloned().unwrap_or_default()
});
}
}
}
17 changes: 5 additions & 12 deletions crates/header-translator/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ use std::path::Path;
use serde::Deserialize;

use crate::data;
use crate::rust_type::Ownership;
use crate::stmt::Derives;
use crate::stmt::{Derives, Mutability};

#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
Expand Down Expand Up @@ -96,9 +95,8 @@ pub struct ClassData {
pub categories: HashMap<String, CategoryData>,
#[serde(default)]
pub derives: Derives,
#[serde(rename = "owned")]
#[serde(default)]
pub ownership: Ownership,
#[serde(skip)]
pub mutability: Mutability,
#[serde(rename = "skipped-protocols")]
#[serde(default)]
pub skipped_protocols: HashSet<String>,
Expand Down Expand Up @@ -156,8 +154,7 @@ pub struct MethodData {
pub unsafe_: bool,
#[serde(default = "skipped_default")]
pub skipped: bool,
#[serde(default = "mutating_default")]
pub mutating: bool,
pub mutating: Option<bool>,
}

#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -191,16 +188,12 @@ fn skipped_default() -> bool {
false
}

fn mutating_default() -> bool {
false
}

impl Default for MethodData {
fn default() -> Self {
Self {
unsafe_: unsafe_default(),
skipped: skipped_default(),
mutating: mutating_default(),
mutating: None,
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/header-translator/src/data/AppKit.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
data! {
// Subclasses `NSMutableAttributedString`, though I think this should
// actually be `InteriorMutable`?
class NSTextStorage: Mutable {}
}
79 changes: 43 additions & 36 deletions crates/header-translator/src/data/Foundation.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
data! {
class NSArray {
// SAFETY: `new` or `initWithObjects:` may choose to deduplicate arrays,
// and returning mutable references to those would be unsound - hence
// `NSArray` cannot be mutable.
class NSArray: ImmutableWithMutableSubclass<Foundation::NSMutableArray> {
unsafe -count;
}

class NSMutableArray: Owned {
unsafe mut -removeAllObjects;
mut -addObject;
mut -insertObject_atIndex;
mut -replaceObjectAtIndex_withObject;
mut -removeObjectAtIndex;
mut -removeLastObject;
mut -sortUsingFunction_context;
class NSMutableArray: MutableWithImmutableSuperclass<Foundation::NSArray> {
unsafe -removeAllObjects;
}

class NSString {
class NSString: ImmutableWithMutableSubclass<Foundation::NSMutableString> {
unsafe -init;
unsafe -compare;
unsafe -hasPrefix;
Expand All @@ -30,59 +27,66 @@ data! {
unsafe +stringWithString;
}

class NSMutableString: Owned {
class NSMutableString: MutableWithImmutableSuperclass<Foundation::NSString> {
unsafe -initWithCapacity;
unsafe +stringWithCapacity;
unsafe -initWithString;
unsafe +stringWithString;
unsafe mut -appendString;
unsafe mut -setString;
unsafe -appendString;
unsafe -setString;
}

class NSAttributedString {
// Allowed to be just `Immutable` since we've removed the `NSCopying` and
// `NSMutableCopying` impls from these for now (they'd return the wrong
// type).
class NSSimpleCString: Immutable {}
class NSConstantString: Immutable {}

class NSAttributedString: ImmutableWithMutableSubclass<Foundation::NSMutableAttributedString> {
unsafe -initWithString;
unsafe -initWithAttributedString;
unsafe -string;
unsafe -length;
}

class NSMutableAttributedString: Owned {
class NSMutableAttributedString: MutableWithImmutableSuperclass<Foundation::NSAttributedString> {
unsafe -initWithString;
unsafe -initWithAttributedString;
unsafe mut -setAttributedString;
unsafe -setAttributedString;
}

class NSBundle {
unsafe +mainBundle;
unsafe -infoDictionary;
}

class NSData {
class NSData: ImmutableWithMutableSubclass<Foundation::NSMutableData> {
unsafe -initWithData;
unsafe +dataWithData;
unsafe -length;
unsafe -bytes;
}

class NSMutableData: Owned {
class NSMutableData: MutableWithImmutableSuperclass<Foundation::NSData> {
unsafe +dataWithData;
unsafe -initWithCapacity;
unsafe +dataWithCapacity;
unsafe mut -setLength;
unsafe mut -mutableBytes;
unsafe -setLength;
unsafe -mutableBytes;
}

class NSDictionary {
// Allowed to be just `Mutable` since we've removed the `NSCopying` and
// `NSMutableCopying` impls from this for now (since they'd return the
// wrong type).
class NSPurgeableData: Mutable {}

class NSDictionary: ImmutableWithMutableSubclass<Foundation::NSMutableDictionary> {
unsafe -count;
unsafe -objectForKey;
unsafe -allKeys;
unsafe -allValues;
}

class NSMutableDictionary: Owned {
unsafe mut -setDictionary;
unsafe mut -removeObjectForKey;
unsafe mut -removeAllObjects;
class NSMutableDictionary: MutableWithImmutableSuperclass<Foundation::NSDictionary> {
unsafe -removeObjectForKey;
unsafe -removeAllObjects;
}

class NSError {
Expand Down Expand Up @@ -130,20 +134,22 @@ data! {
unsafe -operatingSystemVersion;
}

class NSSet {
class NSSet: ImmutableWithMutableSubclass<Foundation::NSMutableSet> {
unsafe -count;
}

class NSMutableSet: Owned {
unsafe mut -removeAllObjects;
mut -addObject;
class NSMutableSet: MutableWithImmutableSuperclass<Foundation::NSSet> {
unsafe -removeAllObjects;
}

class NSMutableCharacterSet: Owned {}
class NSCharacterSet: ImmutableWithMutableSubclass<Foundation::NSMutableCharacterSet> {}
class NSMutableCharacterSet: MutableWithImmutableSuperclass<Foundation::NSCharacterSet> {}

class NSMutableOrderedSet: Owned {}
class NSOrderedSet: ImmutableWithMutableSubclass<Foundation::NSMutableOrderedSet> {}
class NSMutableOrderedSet: MutableWithImmutableSuperclass<Foundation::NSOrderedSet> {}

class NSMutableIndexSet: Owned {}
class NSIndexSet: ImmutableWithMutableSubclass<Foundation::NSMutableIndexSet> {}
class NSMutableIndexSet: MutableWithImmutableSuperclass<Foundation::NSIndexSet> {}

class NSNumber {
unsafe -initWithChar;
Expand Down Expand Up @@ -196,5 +202,6 @@ data! {
unsafe -stringValue;
}

class NSMutableURLRequest: Owned {}
class NSURLRequest: ImmutableWithMutableSubclass<Foundation::NSMutableURLRequest> {}
class NSMutableURLRequest: MutableWithImmutableSuperclass<Foundation::NSURLRequest> {}
}
12 changes: 6 additions & 6 deletions crates/header-translator/src/data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ marked safe, so such an improvement can be made in a minor version!

```rust , ignore
data! {
// Everywhere that the class is returned, it is as
// `Id<MyMutableClass, Owned>`.
//
// Specifying this is _not_ unsafe, but every method that returns this
// class must be checked for safety with it in mind.
class MyMutableClass: Owned {
class MyClass {
// The class method `new` is safe.
unsafe +new;
// The method `init` is safe.
unsafe -init;
}

class MyImmutableClass: ImmutableWithMutableSubclass<MyMutableClass> {}

class MyMutableClass: MutableWithImmutableSuperclass<MyImmutableClass> {
// The method `mutate` takes &mut self, and is safe.
unsafe mut -mutate;
// The method `mutateUnchecked` takes &mut self, but is not safe.
Expand Down
Loading

0 comments on commit 032809f

Please sign in to comment.