Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Enums #1328

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .bazelrc
Expand Up @@ -2,3 +2,4 @@ build --enable_platform_specific_config
build:linux --@rules_rust//:extra_rustc_flags=-Clink-arg=-fuse-ld=lld
build:linux --cxxopt=-std=c++17
build:macos --cxxopt=-std=c++17
build:windows --cxxopt=/std:c++17
4 changes: 4 additions & 0 deletions .vscode/settings.json
@@ -1,5 +1,9 @@
{
"search.exclude": {
"**/target": true
},
"files.associations": {
"variant": "cpp",
"string": "cpp"
}
}
4 changes: 3 additions & 1 deletion demo/build.rs
@@ -1,7 +1,9 @@
fn main() {
cxx_build::bridge("src/main.rs")
.file("src/blobstore.cc")
.flag_if_supported("-std=c++14")
.flag_if_supported("-std=c++17")
.flag_if_supported("/std:c++17")
.flag_if_supported("/Zc:__cplusplus")
.compile("cxxbridge-demo");

println!("cargo:rerun-if-changed=src/main.rs");
Expand Down
5 changes: 5 additions & 0 deletions demo/include/blobstore.h
Expand Up @@ -7,6 +7,7 @@ namespace blobstore {

struct MultiBuf;
struct BlobMetadata;
struct BlobEnum;

class BlobstoreClient {
public:
Expand All @@ -22,5 +23,9 @@ class BlobstoreClient {

std::unique_ptr<BlobstoreClient> new_blobstore_client();

BlobEnum make_enum();
void take_enum(const BlobEnum&);
void take_mut_enum(BlobEnum&);

} // namespace blobstore
} // namespace org
26 changes: 26 additions & 0 deletions demo/src/blobstore.cc
Expand Up @@ -2,6 +2,7 @@
#include "demo/src/main.rs.h"
#include <algorithm>
#include <functional>
#include <iostream>
#include <set>
#include <string>
#include <unordered_map>
Expand Down Expand Up @@ -67,5 +68,30 @@ std::unique_ptr<BlobstoreClient> new_blobstore_client() {
return std::make_unique<BlobstoreClient>();
}

BlobEnum make_enum() { return BlobEnum{false}; }

std::ostream &operator<<(std::ostream &os, const BlobMetadata &md) {
os << "The size [" << md.size << "] and some tags...";
return os;
}

void take_enum(const BlobEnum &enm) {
std::cout << "The index of enum is " << enm.index() << std::endl;
rust::visit(
[](const auto &v) {
std::cout << "The value of enum is " << v << std::endl;
},
enm);
}

void take_mut_enum(BlobEnum &enm) {
take_enum(enm);
if (!::rust::holds_alternative<bool>(enm)) {
enm = false;
} else {
enm = 111;
}
}

} // namespace blobstore
} // namespace org
33 changes: 33 additions & 0 deletions demo/src/main.rs
Expand Up @@ -6,6 +6,20 @@ mod ffi {
tags: Vec<String>,
}

/// A classic.
enum BlobEnum {
/// This is my doc
Bar(i32),
Baz(bool),
Bam(BlobMetadata),
}

enum BlobCLike {
Bar,
Baz,
Bam,
}

// Rust types and signatures exposed to C++.
extern "Rust" {
type MultiBuf;
Expand All @@ -23,6 +37,10 @@ mod ffi {
fn put(&self, parts: &mut MultiBuf) -> u64;
fn tag(&self, blobid: u64, tag: &str);
fn metadata(&self, blobid: u64) -> BlobMetadata;

fn make_enum() -> BlobEnum;
fn take_enum(enm: &BlobEnum);
fn take_mut_enum(enm: &mut BlobEnum);
}
}

Expand All @@ -42,6 +60,21 @@ pub fn next_chunk(buf: &mut MultiBuf) -> &[u8] {
}

fn main() {
let f = ffi::BlobEnum::Bar(1);
ffi::take_enum(&f);
let mut f = ffi::make_enum();
match f {
ffi::BlobEnum::Bar(val) => println!("The value is {val}"),
ffi::BlobEnum::Baz(val) => println!("The value is {val}"),
_ => {}
}
ffi::take_mut_enum(&mut f);
match f {
ffi::BlobEnum::Bar(val) => println!("The value is {val}"),
ffi::BlobEnum::Baz(val) => println!("The value is {val}"),
_ => {}
}

let client = ffi::new_blobstore_client();

// Upload a blob.
Expand Down
4 changes: 2 additions & 2 deletions gen/src/cfg.rs
Expand Up @@ -29,7 +29,7 @@ pub(super) fn strip(
Api::Struct(strct) => strct
.fields
.retain(|field| eval(cx, cfg_errors, cfg_evaluator, &field.cfg)),
Api::Enum(enm) => enm
Api::Enum(enm, _) | Api::EnumUnnamed(enm) => enm
.variants
.retain(|variant| eval(cx, cfg_errors, cfg_evaluator, &variant.cfg)),
_ => {}
Expand Down Expand Up @@ -113,7 +113,7 @@ impl Api {
match self {
Api::Include(include) => &include.cfg,
Api::Struct(strct) => &strct.cfg,
Api::Enum(enm) => &enm.cfg,
Api::Enum(enm, _) | Api::EnumUnnamed(enm) => &enm.cfg,
Api::CxxType(ety) | Api::RustType(ety) => &ety.cfg,
Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.cfg,
Api::TypeAlias(alias) => &alias.cfg,
Expand Down
2 changes: 1 addition & 1 deletion gen/src/namespace.rs
Expand Up @@ -6,7 +6,7 @@ impl Api {
match self {
Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.name.namespace,
Api::CxxType(ety) | Api::RustType(ety) => &ety.name.namespace,
Api::Enum(enm) => &enm.name.namespace,
Api::Enum(enm, _) | Api::EnumUnnamed(enm) => &enm.name.namespace,
Api::Struct(strct) => &strct.name.namespace,
Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => Default::default(),
}
Expand Down
89 changes: 72 additions & 17 deletions gen/src/write.rs
Expand Up @@ -9,8 +9,8 @@ use crate::syntax::set::UnorderedSet;
use crate::syntax::symbol::{self, Symbol};
use crate::syntax::trivial::{self, TrivialReason};
use crate::syntax::{
derive, mangle, Api, Doc, Enum, EnumRepr, ExternFn, ExternType, Pair, Signature, Struct, Trait,
Type, TypeAlias, Types, Var,
derive, mangle, Api, CEnumOpts, Doc, Enum, EnumRepr, ExternFn, ExternType, Pair, Signature,
Struct, Trait, Type, TypeAlias, Types, Var,
};
use proc_macro2::Ident;

Expand All @@ -34,8 +34,8 @@ pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec<u

fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) {
let needs_forward_declaration = |api: &&Api| match api {
Api::Struct(_) | Api::CxxType(_) | Api::RustType(_) => true,
Api::Enum(enm) => !out.types.cxx.contains(&enm.name.rust),
Api::Struct(_) | Api::CxxType(_) | Api::RustType(_) | Api::EnumUnnamed(_) => true,
Api::Enum(enm, _) => !out.types.cxx.contains(&enm.name.rust),
_ => false,
};

Expand All @@ -46,12 +46,12 @@ fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) {

fn write(out: &mut OutFile, ns_entries: &NamespaceEntries, indent: usize) {
let apis = ns_entries.direct_content();

for api in apis {
write!(out, "{:1$}", "", indent);
match api {
Api::Struct(strct) => write_struct_decl(out, &strct.name),
Api::Enum(enm) => write_enum_decl(out, enm),
Api::Enum(enm, opts) => write_enum_decl(out, enm, opts),
Api::EnumUnnamed(enm) => write_struct_decl(out, &enm.name),
Api::CxxType(ety) => write_struct_using(out, &ety.name),
Api::RustType(ety) => write_struct_decl(out, &ety.name),
_ => unreachable!(),
Expand Down Expand Up @@ -99,14 +99,18 @@ fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
}
}
}
Api::Enum(enm) => {
Api::Enum(enm, opts) => {
out.next_section();
if !out.types.cxx.contains(&enm.name.rust) {
write_enum(out, enm);
} else if !enm.variants_from_header {
check_enum(out, enm);
write_enum(out, enm, opts);
} else if !opts.variants_from_header {
check_enum(out, enm, opts);
}
}
Api::EnumUnnamed(enm) => {
out.next_section();
write_enum_unnamed(out, enm);
}
Api::RustType(ety) => {
out.next_section();
let methods = methods_for_type
Expand Down Expand Up @@ -328,8 +332,8 @@ fn write_struct_decl(out: &mut OutFile, ident: &Pair) {
writeln!(out, "struct {};", ident.cxx);
}

fn write_enum_decl(out: &mut OutFile, enm: &Enum) {
let repr = match &enm.repr {
fn write_enum_decl(out: &mut OutFile, enm: &Enum, opts: &CEnumOpts) {
let repr = match &opts.repr {
#[cfg(feature = "experimental-enum-variants-from-header")]
EnumRepr::Foreign { .. } => return,
EnumRepr::Native { atom, .. } => *atom,
Expand All @@ -339,6 +343,56 @@ fn write_enum_decl(out: &mut OutFile, enm: &Enum) {
writeln!(out, ";");
}

fn write_enum_unnamed(out: &mut OutFile, enm: &Enum) {
write!(out, "struct {} final : public ", enm.name.cxx);

/// Writes something like `::rust::variant<type1, type2, ...>` with type1...
/// being cxx types of the enum's variants.
fn write_variants(out: &mut OutFile, enm: &Enum) {
write!(out, "::rust::variant<");
let mut iter = enm.variants.iter().peekable();
while let Some(value) = iter.next() {
match &value.ty {
None => {
write!(out, "::rust::empty");
}
Some(ty) => {
match ty {
Type::Ref(r) => {
// References are not allowed in variants, so we wrap them
// into std::reference_wrapper.
write!(out, "std::reference_wrapper<");
write_type_space(out, &r.inner);
if !r.mutable {
write!(out, "const ");
}
write!(out, ">");
}
_ => write_type(out, ty),
}
}
};
if iter.peek().is_some() {
write!(out, ", ");
}
}
write!(out, ">");
}

write_variants(out, enm);
writeln!(out, "{{");

write!(out, " using base = ");
write_variants(out, enm);
writeln!(out, ";");
writeln!(out, " {}() = delete;", enm.name.cxx);
writeln!(out, " {0}(const {0}&) = default;", enm.name.cxx);
writeln!(out, " {0}({0}&&) = delete;", enm.name.cxx);
writeln!(out, " using base::base;");
writeln!(out, " using base::operator=;");
writeln!(out, "}};");
}

fn write_struct_using(out: &mut OutFile, ident: &Pair) {
writeln!(out, "using {} = {};", ident.cxx, ident.to_fully_qualified());
}
Expand Down Expand Up @@ -388,8 +442,8 @@ fn write_opaque_type<'a>(out: &mut OutFile<'a>, ety: &'a ExternType, methods: &[
writeln!(out, "#endif // {}", guard);
}

fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) {
let repr = match &enm.repr {
fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum, opts: &'a CEnumOpts) {
let repr = match &opts.repr {
#[cfg(feature = "experimental-enum-variants-from-header")]
EnumRepr::Foreign { .. } => return,
EnumRepr::Native { atom, .. } => *atom,
Expand All @@ -410,8 +464,8 @@ fn write_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) {
writeln!(out, "#endif // {}", guard);
}

fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum) {
let repr = match &enm.repr {
fn check_enum<'a>(out: &mut OutFile<'a>, enm: &'a Enum, opts: &'a CEnumOpts) {
let repr = match &opts.repr {
#[cfg(feature = "experimental-enum-variants-from-header")]
EnumRepr::Foreign { .. } => return,
EnumRepr::Native { atom, .. } => *atom,
Expand Down Expand Up @@ -838,7 +892,8 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
write!(out, "(::rust::unsafe_bitcopy, *{})", arg.name.cxx);
} else if out.types.needs_indirect_abi(&arg.ty) {
out.include.utility = true;
write!(out, "::std::move(*{})", arg.name.cxx);
// Let the compiler resolve if this is a copy or a move constructor.
write!(out, "*{}", arg.name.cxx);
} else {
write!(out, "{}", arg.name.cxx);
}
Expand Down