Skip to content

Commit

Permalink
adding OpaqueType instead of OpaqueStruct, and adding tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Ellen Arteca committed Dec 10, 2024
1 parent 6434f0b commit b6dee05
Show file tree
Hide file tree
Showing 25 changed files with 500 additions and 86 deletions.
5 changes: 4 additions & 1 deletion core/src/ast/lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,10 @@ impl LifetimeEnv {
LifetimeEnv::new()
}

pub fn from_enum_item(enm: &syn::ItemEnum, variant_fields: &[(Option<Ident>, TypeName, Docs, Attrs)]) -> Self {
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 {
Expand Down
5 changes: 4 additions & 1 deletion core/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ mod modules;
pub use modules::{File, Module};

mod structs;
pub use structs::{OpaqueStruct, Struct};
pub use structs::Struct;

mod opaque;
pub use opaque::OpaqueType;

mod traits;
pub use traits::{Trait, TraitMethod};
Expand Down
14 changes: 7 additions & 7 deletions core/src/ast/modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use serde::Serialize;
use syn::{ImplItem, Item, ItemMod, UseTree, Visibility};

use super::{
AttrInheritContext, Attrs, CustomType, Enum, Ident, Method, ModSymbol, Mutability,
OpaqueStruct, Path, PathType, RustLink, Struct, Trait,
AttrInheritContext, Attrs, CustomType, Enum, Ident, Method, ModSymbol, Mutability, OpaqueType,
Path, PathType, RustLink, Struct, Trait,
};
use crate::environment::*;

Expand All @@ -18,7 +18,7 @@ enum DiplomatStructAttribute {
/// contain an owned opaque in the form of a `Box`.
Out,
/// An attribute that can correspond to a type (struct or enum).
TypeAttr(DiplomatTypeAttribute)
TypeAttr(DiplomatTypeAttribute),
}

/// Custom Diplomat attribute that can be placed on an enum or struct definition.
Expand Down Expand Up @@ -192,10 +192,10 @@ impl Module {
CustomType::Struct(Struct::new(strct, true, &type_parent_attrs))
}
Ok(Some(DiplomatStructAttribute::TypeAttr(DiplomatTypeAttribute::Opaque))) => {
CustomType::Opaque(OpaqueStruct::new_struct(strct, Mutability::Immutable, &type_parent_attrs))
CustomType::Opaque(OpaqueType::new_struct(strct, Mutability::Immutable, &type_parent_attrs))
}
Ok(Some(DiplomatStructAttribute::TypeAttr(DiplomatTypeAttribute::OpaqueMut))) => {
CustomType::Opaque(OpaqueStruct::new_struct(strct, Mutability::Mutable, &type_parent_attrs))
CustomType::Opaque(OpaqueType::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 @@ -212,10 +212,10 @@ impl Module {
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))
CustomType::Opaque(OpaqueType::new_enum(enm, Mutability::Immutable, &type_parent_attrs))
}
Ok(Some(DiplomatTypeAttribute::OpaqueMut)) => {
CustomType::Opaque(OpaqueStruct::new_enum(enm, Mutability::Mutable, &type_parent_attrs))
CustomType::Opaque(OpaqueType::new_enum(enm, Mutability::Mutable, &type_parent_attrs))
}
Err(errors) => {
panic!("Multiple conflicting Diplomat enum attributes, there can be at most one: {errors:?}");
Expand Down
64 changes: 64 additions & 0 deletions core/src/ast/opaque.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use serde::Serialize;

use super::docs::Docs;
use super::{Attrs, Ident, LifetimeEnv, Method, Mutability};

/// 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]
pub struct OpaqueType {
pub name: Ident,
pub docs: Docs,
pub lifetimes: LifetimeEnv,
pub methods: Vec<Method>,
pub mutability: Mutability,
pub attrs: Attrs,
/// The ABI name of the generated destructor
pub dtor_abi_name: Ident,
}

impl OpaqueType {
/// Extract a [`OpaqueType`] 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);
OpaqueType {
dtor_abi_name: Self::dtor_abi_name(&name, &attrs),
name,
docs: Docs::from_attrs(&strct.attrs),
lifetimes: LifetimeEnv::from_struct_item(strct, &[]),
methods: vec![],
mutability,
attrs,
}
}

/// Extract a [`OpaqueType`] metadata value from an AST node representing an enum.
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);
OpaqueType {
dtor_abi_name: Self::dtor_abi_name(&name, &attrs),
name,
docs: Docs::from_attrs(&enm.attrs),
lifetimes: LifetimeEnv::from_enum_item(enm, &[]),
methods: vec![],
mutability,
attrs,
}
}

fn dtor_abi_name(name: &Ident, attrs: &Attrs) -> Ident {
let dtor_abi_name = format!("{}_destroy", name);
let dtor_abi_name = String::from(attrs.abi_rename.apply(dtor_abi_name.into()));
Ident::from(dtor_abi_name)
}
}
57 changes: 1 addition & 56 deletions core/src/ast/structs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use serde::Serialize;

use super::docs::Docs;
use super::{Attrs, Ident, LifetimeEnv, Method, Mutability, PathType, TypeName};
use super::{Attrs, Ident, LifetimeEnv, Method, PathType, TypeName};

/// A struct declaration in an FFI module that is not opaque.
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Debug)]
Expand Down Expand Up @@ -52,61 +52,6 @@ impl Struct {
}
}

/// 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]
pub struct OpaqueStruct {
pub name: Ident,
pub docs: Docs,
pub lifetimes: LifetimeEnv,
pub methods: Vec<Method>,
pub mutability: Mutability,
pub attrs: Attrs,
/// The ABI name of the generated destructor
pub dtor_abi_name: Ident,
}

impl OpaqueStruct {
/// 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);
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(&strct.attrs),
lifetimes: LifetimeEnv::from_struct_item(strct, &[]),
methods: vec![],
mutability,
attrs,
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)]
mod tests {
use insta::{self, Settings};
Expand Down
6 changes: 3 additions & 3 deletions core/src/ast/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::str::FromStr;

use super::{
Attrs, Docs, Enum, Ident, Lifetime, LifetimeEnv, LifetimeTransitivity, Method, NamedLifetime,
OpaqueStruct, Path, RustLink, Struct, Trait,
OpaqueType, Path, RustLink, Struct, Trait,
};
use crate::Env;

Expand All @@ -19,8 +19,8 @@ use crate::Env;
pub enum CustomType {
/// A non-opaque struct whose fields will be visible across the FFI boundary.
Struct(Struct),
/// A struct annotated with [`diplomat::opaque`] whose fields are not visible.
Opaque(OpaqueStruct),
/// A type annotated with [`diplomat::opaque`] whose fields are not visible.
Opaque(OpaqueType),
/// A fieldless enum.
Enum(Enum),
}
Expand Down
9 changes: 3 additions & 6 deletions core/src/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ impl<'ast> LoweringContext<'ast> {
}
pub(super) fn lower_all_opaques(
&mut self,
ast_defs: impl ExactSizeIterator<Item = ItemAndInfo<'ast, ast::OpaqueStruct>>,
ast_defs: impl ExactSizeIterator<Item = ItemAndInfo<'ast, ast::OpaqueType>>,
) -> Result<Vec<OpaqueDef>, ()> {
self.lower_all(ast_defs, Self::lower_opaque)
}
Expand Down Expand Up @@ -257,10 +257,7 @@ impl<'ast> LoweringContext<'ast> {
Ok(def)
}

fn lower_opaque(
&mut self,
item: ItemAndInfo<'ast, ast::OpaqueStruct>,
) -> Result<OpaqueDef, ()> {
fn lower_opaque(&mut self, item: ItemAndInfo<'ast, ast::OpaqueType>) -> Result<OpaqueDef, ()> {
let ast_opaque = item.item;
self.errors.set_item(ast_opaque.name.as_str());
let name = self.lower_ident(&ast_opaque.name, "opaque name");
Expand Down Expand Up @@ -416,7 +413,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
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
---
source: core/src/hir/type_context.rs
assertion_line: 691
expression: output
---
Lowering error in MyOpaqueEnum::new_broken: Opaque passed by value in input: MyOpaqueEnum
Lowering error in MyOpaqueEnum::do_thing_broken: Method `MyOpaqueEnum_do_thing_broken` takes an opaque by value as the self parameter, but opaques as inputs must be behind refs
Lowering error in MyOpaqueStruct::new_broken: Opaque passed by value in input: MyOpaqueStruct
Lowering error in MyOpaqueStruct::do_thing_broken: Method `MyOpaqueStruct_do_thing_broken` takes an opaque by value as the self parameter, but opaques as inputs must be behind refs

21 changes: 18 additions & 3 deletions core/src/hir/type_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ impl TypeContext {
pub(super) struct LookupId<'ast> {
out_struct_map: HashMap<&'ast ast::Struct, OutStructId>,
struct_map: HashMap<&'ast ast::Struct, StructId>,
opaque_map: HashMap<&'ast ast::OpaqueStruct, OpaqueId>,
opaque_map: HashMap<&'ast ast::OpaqueType, OpaqueId>,
enum_map: HashMap<&'ast ast::Enum, EnumId>,
trait_map: HashMap<&'ast ast::Trait, TraitId>,
}
Expand All @@ -469,7 +469,7 @@ impl<'ast> LookupId<'ast> {
fn new(
out_structs: &[ItemAndInfo<'ast, ast::Struct>],
structs: &[ItemAndInfo<'ast, ast::Struct>],
opaques: &[ItemAndInfo<'ast, ast::OpaqueStruct>],
opaques: &[ItemAndInfo<'ast, ast::OpaqueType>],
enums: &[ItemAndInfo<'ast, ast::Enum>],
traits: &[ItemAndInfo<'ast, ast::Trait>],
) -> Self {
Expand Down Expand Up @@ -510,7 +510,7 @@ impl<'ast> LookupId<'ast> {
self.struct_map.get(strct).copied()
}

pub(super) fn resolve_opaque(&self, opaque: &ast::OpaqueStruct) -> Option<OpaqueId> {
pub(super) fn resolve_opaque(&self, opaque: &ast::OpaqueType) -> Option<OpaqueId> {
self.opaque_map.get(opaque).copied()
}

Expand Down Expand Up @@ -701,6 +701,21 @@ mod tests {
pub fn do_thing_broken(self) {}
pub fn broken_differently(&self, x: &MyOpaqueStruct) {}
}

#[diplomat::opaque]
enum MyOpaqueEnum {
A(UnknownType),
B,
C(i32, i32, UnknownType2),
}

impl MyOpaqueEnum {
pub fn new() -> Box<MyOpaqueEnum> {}
pub fn new_broken() -> MyOpaqueEnum {}
pub fn do_thing(&self) {}
pub fn do_thing_broken(self) {}
pub fn broken_differently(&self, x: &MyOpaqueEnum) {}
}
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions feature_tests/c/include/MyOpaqueEnum.d.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions feature_tests/c/include/MyOpaqueEnum.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit b6dee05

Please sign in to comment.