diff --git a/crates/objc2-encode/CHANGELOG.md b/crates/objc2-encode/CHANGELOG.md index c5f0a22db..8d9400391 100644 --- a/crates/objc2-encode/CHANGELOG.md +++ b/crates/objc2-encode/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased - YYYY-MM-DD +### Fixed +* Allow the encoding of `*mut c_void` in a few places where they would not + otherwise be equivalent. + ## 2.0.0 - 2023-06-20 diff --git a/crates/objc2-encode/src/encoding.rs b/crates/objc2-encode/src/encoding.rs index e7917c046..ec40ca127 100644 --- a/crates/objc2-encode/src/encoding.rs +++ b/crates/objc2-encode/src/encoding.rs @@ -203,6 +203,10 @@ impl Encoding { /// /// See [`Encoding::equivalent_to`] for details about the meaning of /// "equivalence". + /// + /// This differs from [`Encoding::equivalent_to`] in the following: + /// - The encoding `Encoding::Pointer(&Encoding::Void)` (`*mut c_void`) is + /// allowed where other types are expected. pub fn equivalent_to_str(&self, s: &str) -> bool { let mut parser = Parser::new(s); @@ -597,4 +601,21 @@ mod tests { assert!(!enc.equivalent_to_box(&expected)); } + + #[test] + fn pointer_to_void() { + let v = Encoding::Pointer(&Encoding::Void); + let s = Encoding::Pointer(&Encoding::Struct("abc", &[])); + + assert!(v.equivalent_to(&s)); + assert!(s.equivalent_to(&v)); + assert!(v.equivalent_to_str("^{abc=}")); + assert!(s.equivalent_to_str("^v")); + + assert!(!Encoding::Atomic(&Encoding::Struct("abc", &[])).equivalent_to(&v)); + assert!(!Encoding::Atomic(&Encoding::Void).equivalent_to(&s)); + + assert!(!Encoding::Void.equivalent_to_str("{abc=}")); + assert!(!Encoding::Struct("abc", &[]).equivalent_to_str("v")); + } } diff --git a/crates/objc2-encode/src/helper.rs b/crates/objc2-encode/src/helper.rs index aa0a09258..f90a30c2b 100644 --- a/crates/objc2-encode/src/helper.rs +++ b/crates/objc2-encode/src/helper.rs @@ -17,6 +17,13 @@ impl NestingLevel { Self::Top } + pub(crate) const fn should_void_allow_non_void(self) -> bool { + match self { + Self::Top => false, + Self::Bottom | Self::Within => true, + } + } + const fn bitfield(self) -> Self { // This is a bit irrelevant, since bitfields can only contain integral // types @@ -80,9 +87,15 @@ pub(crate) fn compare_encodings( level2 }; - // TODO: Are level1 and level2 ever be different? + // TODO: Are level1 and level2 ever different? match (enc1.helper(level1), enc2.helper(level2)) { + (Primitive(crate::helper::Primitive::Void), _) if level1.should_void_allow_non_void() => { + true + } + (_, Primitive(crate::helper::Primitive::Void)) if level2.should_void_allow_non_void() => { + true + } (Primitive(p1), Primitive(p2)) => p1 == p2, ( BitField(size1, Some((offset1, type1)), level1), diff --git a/crates/objc2-encode/src/parse.rs b/crates/objc2-encode/src/parse.rs index 2360fc25b..6c0080cf8 100644 --- a/crates/objc2-encode/src/parse.rs +++ b/crates/objc2-encode/src/parse.rs @@ -5,7 +5,7 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::fmt; -use crate::helper::{ContainerKind, Helper, NestingLevel}; +use crate::helper::{ContainerKind, Helper, NestingLevel, Primitive}; use crate::{Encoding, EncodingBox}; /// Check whether a struct or union name is a valid identifier @@ -251,7 +251,20 @@ impl Parser<'_> { pub(crate) fn expect_encoding(&mut self, enc: &Encoding, level: NestingLevel) -> Option<()> { let helper = Helper::new(enc, level); + if level.should_void_allow_non_void() && self.expect_byte(b'v').is_some() { + return Some(()); + } match helper { + Helper::Primitive(Primitive::Void) if level.should_void_allow_non_void() => { + if self.expect_byte(b'v').is_some() { + Some(()) + } else { + match self.parse_encoding() { + Ok(_) => Some(()), + Err(_) => None, + } + } + } Helper::Primitive(primitive) => self.expect_str(primitive.to_str()), Helper::BitField(size, Some((offset, t)), level) => { self.expect_byte(b'b')?;