Skip to content

Commit ae51c70

Browse files
committed
Verify class mutability in extern_class! and declare_class!
1 parent 9f393ff commit ae51c70

10 files changed

+385
-4
lines changed

crates/objc2/src/__macro_helpers/declare_class.rs

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ use core::ptr;
33

44
use crate::declare::__IdReturnValue;
55
use crate::rc::{Allocated, Id};
6-
use crate::{Message, MessageReceiver};
6+
use crate::{ClassType, Message, MessageReceiver};
77

88
use super::{CopyOrMutCopy, Init, MaybeUnwrap, New, Other};
9+
use crate::mutability;
910

1011
// One could imagine a different design where we simply had a method like
1112
// `fn convert_receiver()`, but that won't work in `declare_class!` since we
@@ -110,3 +111,70 @@ impl<T: Message> MaybeOptionId for Option<Id<T>> {
110111
__IdReturnValue(ptr.cast())
111112
}
112113
}
114+
115+
/// Helper for ensuring that `ClassType::Mutability` is implemented correctly
116+
/// for subclasses.
117+
pub trait ValidSubclassMutability<T: mutability::Mutability> {}
118+
119+
// Root
120+
impl ValidSubclassMutability<mutability::Immutable> for mutability::Root {}
121+
impl ValidSubclassMutability<mutability::Mutable> for mutability::Root {}
122+
impl<MS, IS> ValidSubclassMutability<mutability::ImmutableWithMutableSubclass<MS>>
123+
for mutability::Root
124+
where
125+
MS: ?Sized + ClassType<Mutability = mutability::MutableWithImmutableSuperclass<IS>>,
126+
IS: ?Sized + ClassType<Mutability = mutability::ImmutableWithMutableSubclass<MS>>,
127+
{
128+
}
129+
impl ValidSubclassMutability<mutability::InteriorMutable> for mutability::Root {}
130+
impl ValidSubclassMutability<mutability::MainThreadOnly> for mutability::Root {}
131+
132+
// Immutable
133+
impl ValidSubclassMutability<mutability::Immutable> for mutability::Immutable {}
134+
135+
// Mutable
136+
impl ValidSubclassMutability<mutability::Mutable> for mutability::Mutable {}
137+
138+
// ImmutableWithMutableSubclass
139+
impl<MS, IS> ValidSubclassMutability<mutability::MutableWithImmutableSuperclass<IS>>
140+
for mutability::ImmutableWithMutableSubclass<MS>
141+
where
142+
MS: ?Sized + ClassType<Mutability = mutability::MutableWithImmutableSuperclass<IS>>,
143+
IS: ?Sized + ClassType<Mutability = mutability::ImmutableWithMutableSubclass<MS>>,
144+
{
145+
}
146+
// Only valid when `NSCopying`/`NSMutableCopying` is not implemented!
147+
impl<MS: ?Sized + ClassType> ValidSubclassMutability<mutability::Immutable>
148+
for mutability::ImmutableWithMutableSubclass<MS>
149+
{
150+
}
151+
152+
// MutableWithImmutableSuperclass
153+
// Only valid when `NSCopying`/`NSMutableCopying` is not implemented!
154+
impl<IS: ?Sized + ClassType> ValidSubclassMutability<mutability::Mutable>
155+
for mutability::MutableWithImmutableSuperclass<IS>
156+
{
157+
}
158+
159+
// InteriorMutable
160+
impl ValidSubclassMutability<mutability::InteriorMutable> for mutability::InteriorMutable {}
161+
impl ValidSubclassMutability<mutability::MainThreadOnly> for mutability::InteriorMutable {}
162+
163+
// MainThreadOnly
164+
impl ValidSubclassMutability<mutability::MainThreadOnly> for mutability::MainThreadOnly {}
165+
166+
/// Ensure that:
167+
/// 1. The type is not a root class (it's superclass implements `ClassType`,
168+
/// and it's mutability is not `Root`), and therefore also implements basic
169+
/// memory management methods, as required by `unsafe impl Message`.
170+
/// 2. The mutability is valid according to the superclass' mutability.
171+
#[inline]
172+
pub fn assert_mutability_matches_superclass_mutability<T>()
173+
where
174+
T: ?Sized + ClassType,
175+
T::Super: ClassType,
176+
T::Mutability: mutability::Mutability,
177+
<T::Super as ClassType>::Mutability: ValidSubclassMutability<T::Mutability>,
178+
{
179+
// Noop
180+
}

crates/objc2/src/__macro_helpers/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ mod cache;
3232
mod declare_class;
3333

3434
pub use self::cache::{CachedClass, CachedSel};
35-
pub use self::declare_class::{MaybeOptionId, MessageRecieveId};
35+
pub use self::declare_class::{
36+
assert_mutability_matches_superclass_mutability, MaybeOptionId, MessageRecieveId,
37+
ValidSubclassMutability,
38+
};
3639

3740
// Common selectors.
3841
//

crates/objc2/src/macros/declare_class.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,8 @@ macro_rules! __inner_declare_class {
533533
const NAME: &'static $crate::__macro_helpers::str = $name_const;
534534

535535
fn class() -> &'static $crate::runtime::Class {
536+
$crate::__macro_helpers::assert_mutability_matches_superclass_mutability::<Self>();
537+
536538
// TODO: Use `core::cell::LazyCell`
537539
static REGISTER_CLASS: $crate::__macro_helpers::Once = $crate::__macro_helpers::Once::new();
538540

crates/objc2/src/macros/extern_class.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,8 @@ macro_rules! __inner_extern_class {
365365

366366
#[inline]
367367
fn class() -> &'static $crate::runtime::Class {
368+
$crate::__macro_helpers::assert_mutability_matches_superclass_mutability::<Self>();
369+
368370
$crate::__class_inner!(
369371
$crate::__select_name!($name; $($name_const)?),
370372
$crate::__hash_idents!($name),
@@ -416,8 +418,12 @@ macro_rules! __extern_class_impl_traits {
416418
// (we even ensure that `Object` is always last in our inheritance
417419
// tree), so it is always safe to reinterpret as that.
418420
//
419-
// That the object must work with standard memory management is upheld
420-
// by the caller.
421+
// That the object must work with standard memory management is
422+
// properly upheld by the fact that the superclass is required by
423+
// `assert_mutability_matches_superclass_mutability` to implement
424+
// `ClassType`, and hence must be a subclass of one of `NSObject`,
425+
// `NSProxy` or some other class that ensures this (e.g. the object
426+
// itself is not a root class).
421427
$(#[$impl_m])*
422428
unsafe impl<$($t)*> $crate::Message for $for {}
423429

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use core::ops::{Deref, DerefMut};
2+
3+
use objc2::encode::{Encoding, RefEncode};
4+
use objc2::runtime::Object;
5+
use objc2::{extern_class, mutability, ClassType, Message};
6+
7+
#[repr(transparent)]
8+
struct MyObject(Object);
9+
10+
unsafe impl RefEncode for MyObject {
11+
const ENCODING_REF: Encoding = Encoding::Object;
12+
}
13+
14+
unsafe impl Message for MyObject {}
15+
16+
impl Deref for MyObject {
17+
type Target = Object;
18+
19+
fn deref(&self) -> &Object {
20+
&self.0
21+
}
22+
}
23+
24+
impl DerefMut for MyObject {
25+
fn deref_mut(&mut self) -> &mut Object {
26+
&mut self.0
27+
}
28+
}
29+
30+
extern_class!(
31+
pub struct MyRootClass;
32+
33+
unsafe impl ClassType for MyRootClass {
34+
type Super = MyObject;
35+
type Mutability = mutability::InteriorMutable;
36+
}
37+
);
38+
39+
fn main() {}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0277]: the trait bound `MyObject: ClassType` is not satisfied
2+
--> ui/extern_class_root.rs
3+
|
4+
| / extern_class!(
5+
| | pub struct MyRootClass;
6+
| |
7+
| | unsafe impl ClassType for MyRootClass {
8+
... |
9+
| | }
10+
| | );
11+
| |_^ the trait `ClassType` is not implemented for `MyObject`
12+
|
13+
= help: the following other types implement trait `ClassType`:
14+
MyRootClass
15+
NSObject
16+
__NSProxy
17+
__RcTestObject
18+
= note: this error originates in the macro `$crate::__inner_extern_class` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use objc2::runtime::Object;
2+
use objc2::{extern_class, mutability, ClassType};
3+
4+
extern_class!(
5+
pub struct MyRootClass;
6+
7+
unsafe impl ClassType for MyRootClass {
8+
type Super = Object;
9+
type Mutability = mutability::InteriorMutable;
10+
}
11+
);
12+
13+
fn main() {}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
error[E0119]: conflicting implementations of trait `AsRef<objc2::runtime::Object>` for type `MyRootClass`
2+
--> ui/extern_class_subclass_object.rs
3+
|
4+
| / extern_class!(
5+
| | pub struct MyRootClass;
6+
| |
7+
| | unsafe impl ClassType for MyRootClass {
8+
... |
9+
| | }
10+
| | );
11+
| | ^
12+
| | |
13+
| |_first implementation here
14+
| conflicting implementation for `MyRootClass`
15+
|
16+
= note: this error originates in the macro `$crate::__impl_as_ref_borrow` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info)
17+
18+
error[E0119]: conflicting implementations of trait `AsMut<objc2::runtime::Object>` for type `MyRootClass`
19+
--> ui/extern_class_subclass_object.rs
20+
|
21+
| / extern_class!(
22+
| | pub struct MyRootClass;
23+
| |
24+
| | unsafe impl ClassType for MyRootClass {
25+
... |
26+
| | }
27+
| | );
28+
| | ^
29+
| | |
30+
| |_first implementation here
31+
| conflicting implementation for `MyRootClass`
32+
|
33+
= note: this error originates in the macro `$crate::__impl_as_ref_borrow` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info)
34+
35+
error[E0119]: conflicting implementations of trait `Borrow<objc2::runtime::Object>` for type `MyRootClass`
36+
--> ui/extern_class_subclass_object.rs
37+
|
38+
| / extern_class!(
39+
| | pub struct MyRootClass;
40+
| |
41+
| | unsafe impl ClassType for MyRootClass {
42+
... |
43+
| | }
44+
| | );
45+
| | ^
46+
| | |
47+
| |_first implementation here
48+
| conflicting implementation for `MyRootClass`
49+
|
50+
= note: this error originates in the macro `$crate::__impl_as_ref_borrow` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info)
51+
52+
error[E0119]: conflicting implementations of trait `BorrowMut<objc2::runtime::Object>` for type `MyRootClass`
53+
--> ui/extern_class_subclass_object.rs
54+
|
55+
| / extern_class!(
56+
| | pub struct MyRootClass;
57+
| |
58+
| | unsafe impl ClassType for MyRootClass {
59+
... |
60+
| | }
61+
| | );
62+
| | ^
63+
| | |
64+
| |_first implementation here
65+
| conflicting implementation for `MyRootClass`
66+
|
67+
= note: this error originates in the macro `$crate::__impl_as_ref_borrow` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use objc2::runtime::NSObject;
2+
use objc2::{extern_class, mutability, ClassType};
3+
4+
extern_class!(
5+
pub struct MyMainThreadClass;
6+
7+
unsafe impl ClassType for MyMainThreadClass {
8+
type Super = NSObject;
9+
type Mutability = mutability::MainThreadOnly;
10+
}
11+
);
12+
13+
extern_class!(
14+
pub struct MyAnyThreadClass;
15+
16+
unsafe impl ClassType for MyAnyThreadClass {
17+
type Super = MyMainThreadClass;
18+
type Mutability = mutability::InteriorMutable;
19+
}
20+
);
21+
22+
extern_class!(
23+
pub struct MyImmutableClass1;
24+
25+
unsafe impl ClassType for MyImmutableClass1 {
26+
type Super = NSObject;
27+
type Mutability = mutability::ImmutableWithMutableSubclass<MyMutableClass1>;
28+
}
29+
);
30+
31+
extern_class!(
32+
pub struct MyMutableClass1;
33+
34+
unsafe impl ClassType for MyMutableClass1 {
35+
type Super = MyImmutableClass1;
36+
type Mutability = mutability::MutableWithImmutableSuperclass<NSObject>;
37+
}
38+
);
39+
40+
extern_class!(
41+
pub struct MyImmutableClass2;
42+
43+
unsafe impl ClassType for MyImmutableClass2 {
44+
type Super = NSObject;
45+
type Mutability = mutability::ImmutableWithMutableSubclass<NSObject>;
46+
}
47+
);
48+
49+
extern_class!(
50+
pub struct MyMutableClass2;
51+
52+
unsafe impl ClassType for MyMutableClass2 {
53+
type Super = MyImmutableClass2;
54+
type Mutability = mutability::MutableWithImmutableSuperclass<MyImmutableClass2>;
55+
}
56+
);
57+
58+
fn main() {}

0 commit comments

Comments
 (0)