Skip to content

Commit

Permalink
opaque enums work, now just need to rename to not be OpaqueStruct
Browse files Browse the repository at this point in the history
  • Loading branch information
Ellen Arteca committed Dec 8, 2024
1 parent 42f8e40 commit 838e061
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 14 deletions.
9 changes: 9 additions & 0 deletions core/src/ast/lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,15 @@ impl LifetimeEnv {
LifetimeEnv::new()
}

pub fn from_enum_item(enm: &syn::ItemEnum, variant_fields: &[(Option<Ident>, TypeName, Docs, Attrs)]) -> Self {
let mut this = LifetimeEnv::new();
this.extend_generics(&enm.generics);
for (_, typ, _, _) in variant_fields {
this.extend_implicit_lifetime_bounds(typ, None);
}
this
}

/// Returns a [`LifetimeEnv`] for a struct, accounding for lifetimes and bounds
/// defined in the struct generics, as well as implicit lifetime bounds in
/// the struct's fields. For example, the field `&'a Foo<'b>` implies `'b: 'a`.
Expand Down
65 changes: 56 additions & 9 deletions core/src/ast/modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,20 @@ enum DiplomatStructAttribute {
/// The `#[diplomat::out]` attribute, used for non-opaque structs that
/// contain an owned opaque in the form of a `Box`.
Out,
/// The `#[diplomat::opaque]` attribute, used for marking a struct as opaque.
/// An attribute that can correspond to a type (struct or enum).
TypeAttr(DiplomatTypeAttribute)
}

/// Custom Diplomat attribute that can be placed on an enum or struct definition.
#[derive(Debug)]
enum DiplomatTypeAttribute {
/// The `#[diplomat::opaque]` attribute, used for marking a type as opaque.
/// Note that opaque structs can be borrowed in return types, but cannot
/// be passed into a function behind a mutable reference.
Opaque,
/// The `#[diplomat::opaque_mut]` attribute, used for marking a struct as
/// The `#[diplomat::opaque_mut]` attribute, used for marking a type as
/// opaque and mutable.
/// Note that mutable opaque structs can never be borrowed in return types
/// Note that mutable opaque types can never be borrowed in return types
/// (even immutably!), but can be passed into a function behind a mutable
/// reference.
OpaqueMut,
Expand All @@ -41,6 +48,35 @@ impl DiplomatStructAttribute {
write!(&mut buf, "{}", attr.path().to_token_stream()).unwrap();
let parsed = match buf.as_str() {
"diplomat :: out" => Some(Self::Out),
"diplomat :: opaque" => Some(Self::TypeAttr(DiplomatTypeAttribute::Opaque)),
"diplomat :: opaque_mut" => Some(Self::TypeAttr(DiplomatTypeAttribute::OpaqueMut)),
_ => None,
};

if let Some(parsed) = parsed {
match res {
Ok(None) => res = Ok(Some(parsed)),
Ok(Some(first)) => res = Err(vec![first, parsed]),
Err(ref mut errors) => errors.push(parsed),
}
}
}

res
}
}

impl DiplomatTypeAttribute {
/// Parses a [`DiplomatTypeAttribute`] from an array of [`syn::Attribute`]s.
/// If more than one kind is found, an error is returned containing all the
/// ones encountered, since all the current attributes are disjoint.
fn parse(attrs: &[syn::Attribute]) -> Result<Option<Self>, Vec<Self>> {
let mut buf = String::with_capacity(32);
let mut res = Ok(None);
for attr in attrs {
buf.clear();
write!(&mut buf, "{}", attr.path().to_token_stream()).unwrap();
let parsed = match buf.as_str() {
"diplomat :: opaque" => Some(Self::Opaque),
"diplomat :: opaque_mut" => Some(Self::OpaqueMut),
_ => None,
Expand Down Expand Up @@ -155,11 +191,11 @@ impl Module {
Ok(Some(DiplomatStructAttribute::Out)) => {
CustomType::Struct(Struct::new(strct, true, &type_parent_attrs))
}
Ok(Some(DiplomatStructAttribute::Opaque)) => {
CustomType::Opaque(OpaqueStruct::new(strct, Mutability::Immutable, &type_parent_attrs))
Ok(Some(DiplomatStructAttribute::TypeAttr(DiplomatTypeAttribute::Opaque))) => {
CustomType::Opaque(OpaqueStruct::new_struct(strct, Mutability::Immutable, &type_parent_attrs))
}
Ok(Some(DiplomatStructAttribute::OpaqueMut)) => {
CustomType::Opaque(OpaqueStruct::new(strct, Mutability::Mutable, &type_parent_attrs))
Ok(Some(DiplomatStructAttribute::TypeAttr(DiplomatTypeAttribute::OpaqueMut))) => {
CustomType::Opaque(OpaqueStruct::new_struct(strct, Mutability::Mutable, &type_parent_attrs))
}
Err(errors) => {
panic!("Multiple conflicting Diplomat struct attributes, there can be at most one: {errors:?}");
Expand All @@ -173,9 +209,20 @@ impl Module {
Item::Enum(enm) => {
if analyze_types {
let ident = (&enm.ident).into();
let enm = Enum::new(enm, &type_parent_attrs);
let custom_enum = match DiplomatTypeAttribute::parse(&enm.attrs[..]) {
Ok(None) => CustomType::Enum(Enum::new(enm, &type_parent_attrs)),
Ok(Some(DiplomatTypeAttribute::Opaque)) => {
CustomType::Opaque(OpaqueStruct::new_enum(enm, Mutability::Immutable, &type_parent_attrs))
}
Ok(Some(DiplomatTypeAttribute::OpaqueMut)) => {
CustomType::Opaque(OpaqueStruct::new_enum(enm, Mutability::Mutable, &type_parent_attrs))
}
Err(errors) => {
panic!("Multiple conflicting Diplomat enum attributes, there can be at most one: {errors:?}");
}
};
custom_types_by_name
.insert(ident, CustomType::Enum(enm));
.insert(ident, custom_enum);
}
}

Expand Down
26 changes: 22 additions & 4 deletions core/src/ast/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ impl Struct {
}
}

/// A struct annotated with [`diplomat::opaque`] whose fields are not visible.
/// Opaque structs cannot be passed by-value across the FFI boundary, so they
/// A type annotated with [`diplomat::opaque`] whose fields/variants are not visible.
/// Opaque types cannot be passed by-value across the FFI boundary, so they
/// must be boxed or passed as references.
#[derive(Clone, Serialize, Debug, Hash, PartialEq, Eq)]
#[non_exhaustive]
Expand All @@ -69,8 +69,8 @@ pub struct OpaqueStruct {
}

impl OpaqueStruct {
/// Extract a [`OpaqueStruct`] metadata value from an AST node.
pub fn new(strct: &syn::ItemStruct, mutability: Mutability, parent_attrs: &Attrs) -> Self {
/// Extract a [`OpaqueStruct`] metadata value from an AST node representing a struct.
pub fn new_struct(strct: &syn::ItemStruct, mutability: Mutability, parent_attrs: &Attrs) -> Self {
let mut attrs = parent_attrs.clone();
attrs.add_attrs(&strct.attrs);
let name = Ident::from(&strct.ident);
Expand All @@ -87,6 +87,24 @@ impl OpaqueStruct {
dtor_abi_name,
}
}

pub fn new_enum(enm: &syn::ItemEnum, mutability: Mutability, parent_attrs: &Attrs) -> Self {
let mut attrs = parent_attrs.clone();
attrs.add_attrs(&enm.attrs);
let name = Ident::from(&enm.ident);
let dtor_abi_name = format!("{}_destroy", name);
let dtor_abi_name = String::from(attrs.abi_rename.apply(dtor_abi_name.into()));
let dtor_abi_name = Ident::from(dtor_abi_name);
OpaqueStruct {
name,
docs: Docs::from_attrs(&enm.attrs),
lifetimes: LifetimeEnv::from_enum_item(enm, &[]),
methods: vec![],
mutability,
attrs,
dtor_abi_name,
}
}
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion core/src/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ impl<'ast> LoweringContext<'ast> {
};
let lifetimes = self.lower_type_lifetime_env(&ast_trait.lifetimes);
let def = TraitDef::new(ast_trait.docs.clone(), trait_name, fcts, attrs, lifetimes?);

self.attr_validator
.validate(&def.attrs, AttributeContext::Trait(&def), &mut self.errors);
Ok(def)
Expand Down

0 comments on commit 838e061

Please sign in to comment.