Support enums defined in C++ code.

This allows listing an enum in an extern "C" block as well as in the
shared block.  In this case, we do not emit the C++ declaration of the
enum but instead emit static assertions that it has the values that we
expect.
This commit is contained in:
Joel Galenson 2020-05-04 14:58:14 -07:00
parent 24d22b471a
commit 905eb2e1f5
3 changed files with 53 additions and 4 deletions

View File

@ -5,7 +5,7 @@ use crate::syntax::namespace::Namespace;
use crate::syntax::symbol::Symbol;
use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var};
use proc_macro2::Ident;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
pub(super) fn gen(
namespace: &Namespace,
@ -46,6 +46,7 @@ pub(super) fn gen(
}
}
let mut cxx_types = HashSet::new();
let mut methods_for_type = HashMap::new();
for api in apis {
if let Api::RustFunction(efn) = api {
@ -56,6 +57,9 @@ pub(super) fn gen(
.push(efn);
}
}
if let Api::CxxType(enm) = api {
cxx_types.insert(&enm.ident);
}
}
for api in apis {
@ -66,7 +70,11 @@ pub(super) fn gen(
}
Api::Enum(enm) => {
out.next_section();
write_enum(out, enm);
if cxx_types.contains(&enm.ident) {
check_enum(out, enm);
} else {
write_enum(out, enm);
}
}
Api::RustType(ety) => {
if let Some(methods) = methods_for_type.get(&ety.ident) {
@ -373,6 +381,34 @@ fn write_enum(out: &mut OutFile, enm: &Enum) {
writeln!(out, "}};");
}
fn check_enum(out: &mut OutFile, enm: &Enum) {
let discriminants = enm
.variants
.iter()
.scan(None, |prev_discriminant, variant| {
let discriminant = variant
.discriminant
.unwrap_or_else(|| prev_discriminant.map_or(0, |n| n + 1));
*prev_discriminant = Some(discriminant);
Some(discriminant)
});
writeln!(
out,
"static_assert(sizeof({}) == sizeof(uint32_t));",
enm.ident
);
enm.variants
.iter()
.zip(discriminants)
.for_each(|(variant, discriminant)| {
writeln!(
out,
"static_assert({}::{} == {});",
enm.ident, variant.ident, discriminant
);
});
}
fn write_exception_glue(out: &mut OutFile, apis: &[Api]) {
let mut has_cxx_throws = false;
for api in apis {

View File

@ -7,6 +7,7 @@ use crate::syntax::{
};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned, ToTokens};
use std::collections::HashSet;
use syn::{parse_quote, Error, ItemMod, Result, Token};
pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result<TokenStream> {
@ -39,12 +40,22 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T
}
}
let mut enums = HashSet::new();
for api in apis {
if let Api::Enum(enm) = api {
enums.insert(&enm.ident);
}
}
for api in apis {
match api {
Api::Include(_) | Api::RustType(_) => {}
Api::Struct(strct) => expanded.extend(expand_struct(strct)),
Api::Enum(enm) => expanded.extend(expand_enum(enm)),
Api::CxxType(ety) => expanded.extend(expand_cxx_type(ety)),
Api::CxxType(ety) => {
if !enums.contains(&ety.ident) {
expanded.extend(expand_cxx_type(ety));
}
}
Api::CxxFunction(efn) => {
expanded.extend(expand_cxx_function_shim(namespace, efn, types));
}

View File

@ -69,7 +69,9 @@ impl<'a> Types<'a> {
}
Api::CxxType(ety) => {
let ident = &ety.ident;
if !type_names.insert(ident) {
// We allow declaring the same type as a shared enum and as a Cxxtype, as this
// means not to emit the C++ enum definition.
if !type_names.insert(ident) && !enums.contains_key(ident) {
duplicate_name(cx, ety, ident);
}
cxx.insert(ident);