Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unstable: extern type support #73

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ jobs:
TESTARGS: ${{ matrix.dinghy && ' ' || '--no-fail-fast' }} ${{ matrix.test-args }}
SOME_FEATURES: ${{ matrix.features || 'malloc,block,exception,foundation' }}
FEATURES: ${{ matrix.features || 'malloc,block,exception,foundation,catch-all,verify_message,uuid' }}
UNSTABLE_FEATURES: ${{ matrix.unstable-features || 'unstable-autoreleasesafe,unstable-c-unwind' }}
UNSTABLE_FEATURES: ${{ matrix.unstable-features || 'unstable-autoreleasesafe,unstable-c-unwind,unstable-extern-types' }}
CMD: cargo

runs-on: ${{ matrix.os }}
Expand Down
15 changes: 9 additions & 6 deletions objc-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,29 @@ default = ["std", "apple"]
std = ["alloc"]
alloc = []

# Link to Apple's objc4
# Link to Apple's objc4.
apple = []

# Link to GNUStep's libobjc2
# Link to GNUStep's libobjc2.
gnustep-1-7 = []
gnustep-1-8 = ["gnustep-1-7"]
gnustep-1-9 = ["gnustep-1-8"]
gnustep-2-0 = ["gnustep-1-9"]
gnustep-2-1 = ["gnustep-2-0"]

# Link to Microsoft's libobjc2
# Link to Microsoft's libobjc2.
unstable-winobjc = ["gnustep-1-8"]

# Link to ObjFW
# Link to ObjFW.
unstable-objfw = []

# Use nightly c_unwind feature
# Use nightly c_unwind feature.
unstable-c-unwind = []

# Private
# Use nightly extern_types feature to mark opaque types as !Sized.
unstable-extern-types = []

# Private.
unstable-exception = ["cc"]
unstable-docsrs = []

Expand Down
19 changes: 13 additions & 6 deletions objc-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#![cfg_attr(feature = "unstable-c-unwind", feature(c_unwind))]
#![cfg_attr(feature = "unstable-docsrs", feature(doc_auto_cfg, doc_cfg_hide))]
#![cfg_attr(feature = "unstable-docsrs", doc(cfg_hide(doc)))]
#![cfg_attr(feature = "unstable-extern-types", feature(extern_types))]

// TODO: Remove this and add "no-std" category to Cargo.toml
// Requires a better solution for C-types in `no_std` crates.
Expand All @@ -41,9 +42,6 @@ compile_error!("The `std` feature currently must be enabled.");
#[doc = include_str!("../README.md")]
extern "C" {}

use core::cell::UnsafeCell;
use core::marker::{PhantomData, PhantomPinned};

macro_rules! generate_linking_tests {
{
extern $abi:literal {$(
Expand Down Expand Up @@ -170,9 +168,18 @@ pub use various::*;
///
/// Downstream libraries can always manually opt in to these types afterwards.
/// (It's also less of a breaking change on our part if we re-add these).
///
/// TODO: Replace this with `extern type` to also mark it as `!Sized`.
type OpaqueData = UnsafeCell<PhantomData<(*const UnsafeCell<()>, PhantomPinned)>>;
#[cfg(not(feature = "unstable-extern-types"))]
type OpaqueData = core::cell::UnsafeCell<
core::marker::PhantomData<(
*const core::cell::UnsafeCell<()>,
core::marker::PhantomPinned,
)>,
>;

#[cfg(feature = "unstable-extern-types")]
extern "C" {
type OpaqueData;
}

#[cfg(test)]
mod tests {
Expand Down
4 changes: 2 additions & 2 deletions objc2-encode/src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,8 +496,8 @@ encode_pointer_impls!(
/// well. So trying to do it quickly requires generating a polynomial amount of
/// implementations, which IMO is overkill for such a small issue.
///
/// Using `?Sized` is probably not safe here because C functions can only take
/// and return items with a known size.
/// Using `?Sized` is not safe here because C functions can only take and
/// return items with a known size.
macro_rules! encode_fn_pointer_impl {
(@ $FnTy: ty, $($Arg: ident),*) => {
unsafe impl<Ret: Encode, $($Arg: Encode),*> Encode for $FnTy {
Expand Down
6 changes: 6 additions & 0 deletions objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* **BREAKING**: Added required `ClassType::NAME` constant for statically
determining the name of a specific class.
* Allow directly specifying class name in declare_class! macro.
* Added the `"unstable-extern-types"` feature flags to make relevant types
`!Sized`.

### Changed
* **BREAKING**: Slightly changed when a type implements `?Sized` to support
`extern type` in the future. In particular, `Message` now requires `Sized`.

### Removed
* **BREAKING**: `MaybeUninit` no longer implements `IvarType` directly; use
Expand Down
23 changes: 14 additions & 9 deletions objc2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ malloc = ["malloc_buf"]
# Expose features that requires creating blocks.
block = ["block2"]

# Runtime selection. See `objc-sys` for details.
apple = ["objc-sys/apple", "objc2-encode/apple", "block2?/apple"]
gnustep-1-7 = ["objc-sys/gnustep-1-7", "objc2-encode/gnustep-1-7", "block2?/gnustep-1-7"]
gnustep-1-8 = ["gnustep-1-7", "objc-sys/gnustep-1-8", "objc2-encode/gnustep-1-8", "block2?/gnustep-1-8"]
gnustep-1-9 = ["gnustep-1-8", "objc-sys/gnustep-1-9", "objc2-encode/gnustep-1-9", "block2?/gnustep-1-9"]
gnustep-2-0 = ["gnustep-1-9", "objc-sys/gnustep-2-0", "objc2-encode/gnustep-2-0", "block2?/gnustep-2-0"]
gnustep-2-1 = ["gnustep-2-0", "objc-sys/gnustep-2-1", "objc2-encode/gnustep-2-1", "block2?/gnustep-2-1"]

# Make the `sel!` macro look up the selector statically.
#
# The plan is to enable this by default, but right now we are uncertain of
Expand All @@ -69,16 +77,13 @@ unstable-autoreleasesafe = []
# `objc2-encode/unstable-c-unwind` to use this.
unstable-c-unwind = []

# For better documentation on docs.rs
unstable-docsrs = []
# Use nightly features to make Object and other types !Sized.
#
# You must manually enable `objc-sys/unstable-extern-types` to use this.
unstable-extern-types = []

# Runtime selection. See `objc-sys` for details.
apple = ["objc-sys/apple", "objc2-encode/apple", "block2?/apple"]
gnustep-1-7 = ["objc-sys/gnustep-1-7", "objc2-encode/gnustep-1-7", "block2?/gnustep-1-7"]
gnustep-1-8 = ["gnustep-1-7", "objc-sys/gnustep-1-8", "objc2-encode/gnustep-1-8", "block2?/gnustep-1-8"]
gnustep-1-9 = ["gnustep-1-8", "objc-sys/gnustep-1-9", "objc2-encode/gnustep-1-9", "block2?/gnustep-1-9"]
gnustep-2-0 = ["gnustep-1-9", "objc-sys/gnustep-2-0", "objc2-encode/gnustep-2-0", "block2?/gnustep-2-0"]
gnustep-2-1 = ["gnustep-2-0", "objc-sys/gnustep-2-1", "objc2-encode/gnustep-2-1", "block2?/gnustep-2-1"]
# Private.
unstable-docsrs = []

[dependencies]
malloc_buf = { version = "1.0", optional = true }
Expand Down
22 changes: 14 additions & 8 deletions objc2/src/__macro_helpers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::__sel_inner;
use crate::rc::{Allocated, Id, Ownership};
use crate::runtime::{Class, Object, Sel};
use crate::{Message, MessageArguments, MessageReceiver};
use crate::{Message, MessageArguments, MessageReceiver, Thin};

pub use crate::cache::CachedClass;
pub use crate::cache::CachedSel;
Expand Down Expand Up @@ -83,7 +83,7 @@ type Init = RetainSemantics<false, false, true, false>;
type CopyOrMutCopy = RetainSemantics<false, false, false, true>;
type Other = RetainSemantics<false, false, false, false>;

pub trait MsgSendId<T, U: ?Sized, O: Ownership> {
pub trait MsgSendId<T, U: ?Sized + Thin, O: Ownership> {
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<U, O>>(
obj: T,
sel: Sel,
Expand All @@ -107,7 +107,10 @@ impl<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, T, O> for New {
}
}

impl<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, Allocated<T>, O> for Alloc {
impl<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, Allocated<T>, O> for Alloc
where
Allocated<T>: Thin,
{
#[inline]
#[track_caller]
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<Allocated<T>, O>>(
Expand All @@ -123,7 +126,10 @@ impl<T: ?Sized + Message, O: Ownership> MsgSendId<&'_ Class, Allocated<T>, O> fo
}
}

impl<T: ?Sized + Message, O: Ownership> MsgSendId<Option<Id<Allocated<T>, O>>, T, O> for Init {
impl<T: ?Sized + Message, O: Ownership> MsgSendId<Option<Id<Allocated<T>, O>>, T, O> for Init
where
Allocated<T>: Thin,
{
#[inline]
#[track_caller]
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<T, O>>(
Expand Down Expand Up @@ -162,7 +168,7 @@ impl<T: MessageReceiver, U: ?Sized + Message, O: Ownership> MsgSendId<T, U, O> f
}
}

impl<T: MessageReceiver, U: Message, O: Ownership> MsgSendId<T, U, O> for Other {
impl<T: MessageReceiver, U: ?Sized + Message, O: Ownership> MsgSendId<T, U, O> for Other {
#[inline]
#[track_caller]
unsafe fn send_message_id<A: MessageArguments, R: MaybeUnwrap<U, O>>(
Expand All @@ -186,14 +192,14 @@ impl<T: MessageReceiver, U: Message, O: Ownership> MsgSendId<T, U, O> for Other
}
}

pub trait MaybeUnwrap<T: ?Sized, O: Ownership> {
pub trait MaybeUnwrap<T: ?Sized + Thin, O: Ownership> {
fn maybe_unwrap<'a, Failed: MsgSendIdFailed<'a>>(
obj: Option<Id<T, O>>,
args: Failed::Args,
) -> Self;
}

impl<T: ?Sized, O: Ownership> MaybeUnwrap<T, O> for Option<Id<T, O>> {
impl<T: ?Sized + Thin, O: Ownership> MaybeUnwrap<T, O> for Option<Id<T, O>> {
#[inline]
#[track_caller]
fn maybe_unwrap<'a, Failed: MsgSendIdFailed<'a>>(
Expand All @@ -204,7 +210,7 @@ impl<T: ?Sized, O: Ownership> MaybeUnwrap<T, O> for Option<Id<T, O>> {
}
}

impl<T: ?Sized, O: Ownership> MaybeUnwrap<T, O> for Id<T, O> {
impl<T: ?Sized + Thin, O: Ownership> MaybeUnwrap<T, O> for Id<T, O> {
#[inline]
#[track_caller]
fn maybe_unwrap<'a, Failed: MsgSendIdFailed<'a>>(
Expand Down
13 changes: 7 additions & 6 deletions objc2/src/cache.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use core::ffi::c_void;
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};

Expand All @@ -7,7 +8,7 @@ use crate::runtime::{Class, Sel};
/// Allows storing a [`Sel`] in a static and lazily loading it.
#[doc(hidden)]
pub struct CachedSel {
ptr: AtomicPtr<ffi::objc_selector>,
ptr: AtomicPtr<c_void>,
}

impl CachedSel {
Expand All @@ -24,7 +25,7 @@ impl CachedSel {
#[doc(hidden)]
pub unsafe fn get(&self, name: &str) -> Sel {
// `Relaxed` should be fine since `sel_registerName` is thread-safe.
let ptr = self.ptr.load(Ordering::Relaxed);
let ptr: *const ffi::objc_selector = self.ptr.load(Ordering::Relaxed).cast();
unsafe { Sel::from_ptr(ptr) }.unwrap_or_else(|| {
// The panic inside `Sel::register_unchecked` is unfortunate, but
// strict correctness is more important than speed
Expand All @@ -34,7 +35,7 @@ impl CachedSel {
// We know this, because we construct it in `sel!` ourselves
let sel = unsafe { Sel::register_unchecked(name.as_ptr().cast()) };
self.ptr
.store(sel.as_ptr() as *mut ffi::objc_selector, Ordering::Relaxed);
.store(sel.as_ptr() as *mut c_void, Ordering::Relaxed);
sel
})
}
Expand All @@ -43,7 +44,7 @@ impl CachedSel {
/// Allows storing a [`Class`] reference in a static and lazily loading it.
#[doc(hidden)]
pub struct CachedClass {
ptr: AtomicPtr<Class>,
ptr: AtomicPtr<c_void>,
}

impl CachedClass {
Expand All @@ -60,12 +61,12 @@ impl CachedClass {
#[doc(hidden)]
pub unsafe fn get(&self, name: &str) -> Option<&'static Class> {
// `Relaxed` should be fine since `objc_getClass` is thread-safe.
let ptr = self.ptr.load(Ordering::Relaxed);
let ptr: *const Class = self.ptr.load(Ordering::Relaxed).cast();
if let Some(cls) = unsafe { ptr.as_ref() } {
Some(cls)
} else {
let ptr: *const Class = unsafe { ffi::objc_getClass(name.as_ptr().cast()) }.cast();
self.ptr.store(ptr as *mut Class, Ordering::Relaxed);
self.ptr.store(ptr as *mut c_void, Ordering::Relaxed);
unsafe { ptr.as_ref() }
}
}
Expand Down
2 changes: 1 addition & 1 deletion objc2/src/class_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub unsafe trait ClassType: Message {
/// [`Deref`]: std::ops::Deref
/// [`Deref::Target`]: std::ops::Deref::Target
/// [`runtime::Object`]: crate::runtime::Object
type Super: Message;
type Super: ?Sized + Message;

/// The name of the Objective-C class that this type represents.
const NAME: &'static str;
Expand Down
10 changes: 5 additions & 5 deletions objc2/src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ use crate::encode::{Encode, EncodeArguments, Encoding, RefEncode};
use crate::ffi;
use crate::runtime::{Bool, Class, Imp, Object, Protocol, Sel};
use crate::sel;
use crate::Message;
use crate::{Message, Thin};

pub use ivar::{InnerIvarType, Ivar, IvarType};
pub use ivar_drop::IvarDrop;
Expand All @@ -142,7 +142,7 @@ pub(crate) mod private {
/// function pointer types.
pub trait MethodImplementation: private::Sealed {
/// The callee type of the method.
type Callee: RefEncode + ?Sized;
type Callee: RefEncode + ?Sized + Thin;
/// The return type of the method.
type Ret: Encode;
/// The argument types of the method.
Expand All @@ -156,14 +156,14 @@ macro_rules! method_decl_impl {
(@<$($l:lifetime),*> T, $r:ident, $f:ty, $($t:ident),*) => {
impl<$($l,)* T, $r, $($t),*> private::Sealed for $f
where
T: Message + ?Sized,
T: ?Sized + Message,
$r: Encode,
$($t: Encode,)*
{}

impl<$($l,)* T, $r, $($t),*> MethodImplementation for $f
where
T: Message + ?Sized,
T: ?Sized + Message,
$r: Encode,
$($t: Encode,)*
{
Expand Down Expand Up @@ -336,7 +336,7 @@ impl ClassBuilder {
/// when the method is invoked from Objective-C.
pub unsafe fn add_method<T, F>(&mut self, sel: Sel, func: F)
where
T: Message + ?Sized,
T: ?Sized + Message,
F: MethodImplementation<Callee = T>,
{
let encs = F::Args::ENCODINGS;
Expand Down
11 changes: 7 additions & 4 deletions objc2/src/declare/ivar_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ unsafe impl<T: Sized> InnerIvarType for IvarDrop<Option<Box<T>>> {
}
}

impl<T: Message, O: Ownership> super::ivar::private::Sealed for IvarDrop<Id<T, O>> {}
impl<T: ?Sized + Message, O: Ownership> super::ivar::private::Sealed for IvarDrop<Id<T, O>> {}
// SAFETY: `Id` is `NonNull<T>`, and hence safe to store as a pointer.
//
// The user ensures that the Id has been initialized in an `init` method
Expand All @@ -107,7 +107,7 @@ impl<T: Message, O: Ownership> super::ivar::private::Sealed for IvarDrop<Id<T, O
// Note: We could technically do `impl InnerIvarType for Ivar<Id<T, O>>`
// directly today, but since we can't do so for `Box` (because that is
// `#[fundamental]`), I think it makes sense to handle them similarly.
unsafe impl<T: Message, O: Ownership> InnerIvarType for IvarDrop<Id<T, O>> {
unsafe impl<T: ?Sized + Message, O: Ownership> InnerIvarType for IvarDrop<Id<T, O>> {
const __ENCODING: Encoding = <*const T as EncodeConvert>::__ENCODING;

type __Inner = Option<Id<T, O>>;
Expand Down Expand Up @@ -137,12 +137,15 @@ unsafe impl<T: Message, O: Ownership> InnerIvarType for IvarDrop<Id<T, O>> {
}
}

impl<T: Message, O: Ownership> super::ivar::private::Sealed for IvarDrop<Option<Id<T, O>>> {}
impl<T: ?Sized + Message, O: Ownership> super::ivar::private::Sealed
for IvarDrop<Option<Id<T, O>>>
{
}
// SAFETY: `Id<T, O>` guarantees the null-pointer optimization.
//
// This is valid to initialize as all-zeroes, so the user doesn't have to do
// anything to initialize it.
unsafe impl<T: Message, O: Ownership> InnerIvarType for IvarDrop<Option<Id<T, O>>> {
unsafe impl<T: ?Sized + Message, O: Ownership> InnerIvarType for IvarDrop<Option<Id<T, O>>> {
const __ENCODING: Encoding = <*const T as EncodeConvert>::__ENCODING;

type __Inner = Option<Id<T, O>>;
Expand Down
Loading