diff --git a/crates/objc2/src/__macro_helpers/class.rs b/crates/objc2/src/__macro_helpers/class.rs index 28b3cf821..c38a89bff 100644 --- a/crates/objc2/src/__macro_helpers/class.rs +++ b/crates/objc2/src/__macro_helpers/class.rs @@ -47,6 +47,26 @@ impl MainThreadOnlyDoesNotImplSendSync MainThreadOnlyDoesNotImplSendSync for Cls {} +/// Check that class does not implement `Drop`. +/// +/// This is not needed for soundness, it's just a nice footgun to avoid (since +/// it wouldn't ever get called). +/// +/// Check implemented using type inference: +/// let _ = >::check +pub trait DoesNotImplDrop { + // Required to reference the trait. + fn check() {} +} + +// Type inference will find this blanket impl... +impl DoesNotImplDrop<()> for Cls {} + +// ... unless this impl also applies, then type inference fails. +struct ImplsDrop; +#[allow(drop_bounds)] // We're intentionally using `Drop` as a bound. +impl DoesNotImplDrop for Cls {} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/objc2/src/__macro_helpers/mod.rs b/crates/objc2/src/__macro_helpers/mod.rs index ee51e805f..380148904 100644 --- a/crates/objc2/src/__macro_helpers/mod.rs +++ b/crates/objc2/src/__macro_helpers/mod.rs @@ -30,7 +30,7 @@ mod sync_unsafe_cell; mod writeback; pub use self::cache::{CachedClass, CachedSel}; -pub use self::class::{MainThreadOnlyDoesNotImplSendSync, ValidThreadKind}; +pub use self::class::{DoesNotImplDrop, MainThreadOnlyDoesNotImplSendSync, ValidThreadKind}; pub use self::common_selectors::{alloc_sel, dealloc_sel, init_sel, new_sel}; pub use self::convert::{ConvertArgument, ConvertArguments, ConvertReturn, TupleExtender}; pub use self::declare_class::{ diff --git a/crates/objc2/src/macros/extern_class.rs b/crates/objc2/src/macros/extern_class.rs index da363d73c..01c78903e 100644 --- a/crates/objc2/src/macros/extern_class.rs +++ b/crates/objc2/src/macros/extern_class.rs @@ -300,6 +300,7 @@ macro_rules! __extern_class_inner { fn class() -> &'static $crate::runtime::AnyClass { let _ = ::ThreadKind>>::check; let _ = >::check; + let _ = >::check; $crate::__class_inner!($crate::__fallback_if_not_set! { ($($name)*) diff --git a/crates/test-ui/ui/extern_class_impls_drop.rs b/crates/test-ui/ui/extern_class_impls_drop.rs new file mode 100644 index 000000000..3d8c35d99 --- /dev/null +++ b/crates/test-ui/ui/extern_class_impls_drop.rs @@ -0,0 +1,14 @@ +//! Test that implementing Drop for a class created with extern_class! fails. +use objc2::extern_class; +use objc2::runtime::NSObject; + +extern_class!( + #[unsafe(super(NSObject))] + struct MyClass; +); + +impl Drop for MyClass { + fn drop(&mut self) {} +} + +fn main() {} diff --git a/crates/test-ui/ui/extern_class_impls_drop.stderr b/crates/test-ui/ui/extern_class_impls_drop.stderr new file mode 100644 index 000000000..3325452f0 --- /dev/null +++ b/crates/test-ui/ui/extern_class_impls_drop.stderr @@ -0,0 +1,15 @@ +error[E0283]: type annotations needed + --> ui/extern_class_impls_drop.rs + | + | / extern_class!( + | | #[unsafe(super(NSObject))] + | | struct MyClass; + | | ); + | |_^ cannot infer type + | + = note: multiple `impl`s satisfying `MyClass: DoesNotImplDrop<_>` found in the `objc2` crate: + - impl DoesNotImplDrop<()> for Cls + where Cls: ?Sized; + - impl DoesNotImplDrop<__macro_helpers::class::ImplsDrop> for Cls + where Cls: Drop, Cls: ?Sized; + = note: this error originates in the macro `$crate::__extern_class_inner` which comes from the expansion of the macro `extern_class` (in Nightly builds, run with -Z macro-backtrace for more info)