Skip to content

Commit daffc74

Browse files
committed
Add expansion of shared type aliases and unit tests
1 parent c2d2316 commit daffc74

File tree

8 files changed

+155
-15
lines changed

8 files changed

+155
-15
lines changed

macro/src/expand.rs

+17-4
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
4141
for api in apis {
4242
match api {
4343
Api::Include(_) | Api::RustType(_) => {}
44-
Api::Struct(strct) => expanded.extend(expand_struct(strct)),
45-
Api::Enum(enm) => expanded.extend(expand_enum(enm)),
44+
Api::Struct(strct) => expanded.extend(expand_struct(namespace, strct)),
45+
Api::Enum(enm) => expanded.extend(expand_enum(namespace, enm)),
4646
Api::CxxType(ety) => {
4747
let ident = &ety.ident;
4848
if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) {
@@ -120,7 +120,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
120120
}
121121
}
122122

123-
fn expand_struct(strct: &Struct) -> TokenStream {
123+
fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream {
124124
let ident = &strct.ident;
125125
let doc = &strct.doc;
126126
let derives = &strct.derives;
@@ -130,17 +130,24 @@ fn expand_struct(strct: &Struct) -> TokenStream {
130130
let vis = Token![pub](field.ident.span());
131131
quote!(#vis #field)
132132
});
133+
let type_id = type_id(namespace, ident);
134+
// TODO: Add ui test for mismatched alias kinds
133135
quote! {
134136
#doc
135137
#[derive(#(#derives),*)]
136138
#[repr(C)]
137139
pub struct #ident {
138140
#(#fields,)*
139141
}
142+
143+
unsafe impl ::cxx::ExternType for #ident {
144+
type Kind = ::cxx::extern_type::KindShared;
145+
type Id = #type_id;
146+
}
140147
}
141148
}
142149

143-
fn expand_enum(enm: &Enum) -> TokenStream {
150+
fn expand_enum(namespace: &Namespace, enm: &Enum) -> TokenStream {
144151
let ident = &enm.ident;
145152
let doc = &enm.doc;
146153
let repr = enm.repr;
@@ -151,6 +158,7 @@ fn expand_enum(enm: &Enum) -> TokenStream {
151158
pub const #variant_ident: Self = #ident { repr: #discriminant };
152159
})
153160
});
161+
let type_id = type_id(namespace, ident);
154162
quote! {
155163
#doc
156164
#[derive(Copy, Clone, PartialEq, Eq)]
@@ -163,6 +171,11 @@ fn expand_enum(enm: &Enum) -> TokenStream {
163171
impl #ident {
164172
#(#variants)*
165173
}
174+
175+
unsafe impl ::cxx::ExternType for #ident {
176+
type Kind = ::cxx::extern_type::KindShared;
177+
type Id = #type_id;
178+
}
166179
}
167180
}
168181

syntax/check.rs

+3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ fn check_type_ident(cx: &mut Check, ident: &Ident) {
6464
&& !cx.types.enums.contains_key(ident)
6565
&& !cx.types.cxx.contains(ident)
6666
&& !cx.types.rust.contains(ident)
67+
&& !cx.types.aliases.contains_key(ident)
6768
{
6869
cx.error(ident, "unsupported type");
6970
}
@@ -230,6 +231,8 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
230231
&& !cx.types.cxx.contains(&receiver.ty)
231232
&& !cx.types.rust.contains(&receiver.ty)
232233
{
234+
// TODO: Add ui test for aliases being disallowed in receiver position since we can't
235+
// tell if it's a struct or enum, unique error message
233236
cx.error(span, "unrecognized receiver type");
234237
}
235238

syntax/types.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::syntax::atom::Atom::{self, *};
22
use crate::syntax::report::Errors;
33
use crate::syntax::set::OrderedSet as Set;
4-
use crate::syntax::{Api, Derive, Enum, ExternType, Struct, Type, TypeAlias};
4+
use crate::syntax::{AliasKind, Api, Derive, Enum, ExternType, Struct, Type, TypeAlias};
55
use proc_macro2::Ident;
66
use quote::ToTokens;
77
use std::collections::{BTreeMap as Map, HashSet as UnorderedSet};
@@ -129,7 +129,9 @@ impl<'a> Types<'a> {
129129
if !type_names.insert(ident) {
130130
duplicate_name(cx, alias, ident);
131131
}
132-
cxx.insert(ident);
132+
if let AliasKind::OpaqueCpp = alias.kind {
133+
cxx.insert(ident);
134+
}
133135
aliases.insert(ident, alias);
134136
}
135137
}

tests/ffi/alias.rs

+41
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,52 @@
33
#[rustfmt::skip]
44
#[cxx::bridge(namespace = tests)]
55
pub mod ffi {
6+
type Shared = crate::ffi::Shared;
7+
type Enum = crate::ffi::Enum;
8+
69
extern "C" {
710
include!("cxx-test-suite/tests.h");
11+
// Need complete C++ type declaration generated by other bridge for aliased shared types.
12+
include!("cxx-test-suite/lib.rs.h");
813

914
type C = crate::ffi::C;
1015

16+
// TODO: The alias prefix can be removed once these are in their own namespace.
17+
fn alias_c_return_shared() -> Shared;
18+
fn alias_c_return_ref(shared: &Shared) -> &usize;
19+
fn alias_c_return_mut(shared: &mut Shared) -> &mut usize;
20+
fn alias_c_return_enum(n: u16) -> Enum;
21+
fn alias_c_return_unique_ptr_vector_shared() -> UniquePtr<CxxVector<Shared>>;
22+
23+
fn alias_c_take_shared(shared: Shared);
24+
// TODO: This don't work yet because both bridges try to emit the rust_vec$tests$Shared
25+
// functions. Need the remote bridge to always emit if this is gonna work. Or can we work
26+
// around ODR by making the rust_vec functions inline?
27+
//fn alias_c_take_rust_vec_shared(v: Vec<Shared>);
28+
// TODO: Box<Shared> probably has the same problem, not currently tested
29+
// TODO: Same for UniquePtr<Shared>, not currently tested
30+
// TODO: Below probably doesn't work if the remote bridge doesn't use CxxVector<Shared>
31+
fn alias_c_take_unique_ptr_vector_shared(v: UniquePtr<CxxVector<Shared>>);
32+
fn alias_c_take_enum(e: Enum);
33+
1134
fn c_take_unique_ptr(c: UniquePtr<C>);
1235
}
36+
37+
extern "Rust" {
38+
// TODO: The alias prefix can be removed once these are in their own namespace.
39+
fn alias_r_return_shared() -> Shared;
40+
fn alias_r_return_ref(shared: &Shared) -> &usize;
41+
fn alias_r_return_mut(shared: &mut Shared) -> &mut usize;
42+
fn alias_r_return_enum(n: u32) -> Enum;
43+
44+
fn alias_r_take_shared(shared: Shared);
45+
fn alias_r_take_enum(e: Enum);
46+
}
1347
}
48+
49+
use crate::r_return_enum as alias_r_return_enum;
50+
use crate::r_return_mut as alias_r_return_mut;
51+
use crate::r_return_ref as alias_r_return_ref;
52+
use crate::r_return_shared as alias_r_return_shared;
53+
use crate::r_take_enum as alias_r_take_enum;
54+
use crate::r_take_shared as alias_r_take_shared;

tests/ffi/lib.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ fn r_return_primitive() -> usize {
190190
2020
191191
}
192192

193-
fn r_return_shared() -> ffi::Shared {
193+
pub(crate) fn r_return_shared() -> ffi::Shared {
194194
ffi::Shared { z: 2020 }
195195
}
196196

@@ -205,11 +205,11 @@ fn r_return_unique_ptr() -> UniquePtr<ffi::C> {
205205
unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr()) }
206206
}
207207

208-
fn r_return_ref(shared: &ffi::Shared) -> &usize {
208+
pub(crate) fn r_return_ref(shared: &ffi::Shared) -> &usize {
209209
&shared.z
210210
}
211211

212-
fn r_return_mut(shared: &mut ffi::Shared) -> &mut usize {
212+
pub(crate) fn r_return_mut(shared: &mut ffi::Shared) -> &mut usize {
213213
&mut shared.z
214214
}
215215

@@ -255,7 +255,7 @@ fn r_return_sum(n1: usize, n2: usize) -> usize {
255255
n1 + n2
256256
}
257257

258-
fn r_return_enum(n: u32) -> ffi::Enum {
258+
pub(crate) fn r_return_enum(n: u32) -> ffi::Enum {
259259
if n == 0 {
260260
ffi::Enum::AVal
261261
} else if n <= 2020 {
@@ -269,7 +269,7 @@ fn r_take_primitive(n: usize) {
269269
assert_eq!(n, 2020);
270270
}
271271

272-
fn r_take_shared(shared: ffi::Shared) {
272+
pub(crate) fn r_take_shared(shared: ffi::Shared) {
273273
assert_eq!(shared.z, 2020);
274274
}
275275

@@ -322,7 +322,7 @@ fn r_take_ref_rust_vec_string(v: &Vec<String>) {
322322
let _ = v;
323323
}
324324

325-
fn r_take_enum(e: ffi::Enum) {
325+
pub(crate) fn r_take_enum(e: ffi::Enum) {
326326
let _ = e;
327327
}
328328

tests/ffi/tests.cc

+29-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "cxx-test-suite/tests.h"
2+
#include "cxx-test-suite/alias.rs.h"
23
#include "cxx-test-suite/lib.rs.h"
34
#include <cstring>
45
#include <iterator>
@@ -357,15 +358,16 @@ const rust::Vec<uint8_t> &c_try_return_ref_rust_vec(const C &c) {
357358
throw std::runtime_error("unimplemented");
358359
}
359360

360-
extern "C" C *cxx_test_suite_get_unique_ptr() noexcept {
361+
extern "C" {
362+
363+
C *cxx_test_suite_get_unique_ptr() noexcept {
361364
return std::unique_ptr<C>(new C{2020}).release();
362365
}
363366

364-
extern "C" std::string *cxx_test_suite_get_unique_ptr_string() noexcept {
367+
std::string *cxx_test_suite_get_unique_ptr_string() noexcept {
365368
return std::unique_ptr<std::string>(new std::string("2020")).release();
366369
}
367370

368-
extern "C" const char *cxx_run_test() noexcept {
369371
#define STRINGIFY(x) #x
370372
#define TOSTRING(x) STRINGIFY(x)
371373
#define ASSERT(x) \
@@ -375,6 +377,7 @@ extern "C" const char *cxx_run_test() noexcept {
375377
} \
376378
} while (false)
377379

380+
const char *cxx_run_test() noexcept {
378381
ASSERT(r_return_primitive() == 2020);
379382
ASSERT(r_return_shared().z == 2020);
380383
ASSERT(cxx_test_suite_r_is_correct(&*r_return_box()));
@@ -427,4 +430,27 @@ extern "C" const char *cxx_run_test() noexcept {
427430
return nullptr;
428431
}
429432

433+
const char *cxx_run_alias_test() noexcept {
434+
ASSERT(alias_r_return_shared().z == 2020);
435+
ASSERT(alias_r_return_ref(Shared{2020}) == 2020);
436+
{
437+
Shared shared{2019};
438+
size_t &mut_z = alias_r_return_mut(shared);
439+
ASSERT(mut_z == 2019);
440+
mut_z = 2020;
441+
ASSERT(alias_r_return_ref(shared) == 2020);
442+
}
443+
ASSERT(alias_r_return_enum(0) == Enum::AVal);
444+
ASSERT(alias_r_return_enum(1) == Enum::BVal);
445+
ASSERT(alias_r_return_enum(2021) == Enum::CVal);
446+
447+
r_take_shared(Shared{2020});
448+
r_take_enum(Enum::AVal);
449+
450+
cxx_test_suite_set_correct();
451+
return nullptr;
452+
}
453+
454+
} // extern "C"
455+
430456
} // namespace tests

tests/ffi/tests.h

+11
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,15 @@ rust::Vec<uint8_t> c_try_return_rust_vec();
101101
rust::Vec<rust::String> c_try_return_rust_vec_string();
102102
const rust::Vec<uint8_t> &c_try_return_ref_rust_vec(const C &c);
103103

104+
const auto alias_c_return_shared = c_return_shared;
105+
const auto alias_c_return_ref = c_return_ref;
106+
const auto alias_c_return_mut = c_return_mut;
107+
const auto alias_c_return_enum = c_return_enum;
108+
const auto alias_c_return_unique_ptr_vector_shared =
109+
c_return_unique_ptr_vector_shared;
110+
const auto alias_c_take_unique_ptr_vector_shared =
111+
c_take_unique_ptr_vector_shared;
112+
const auto alias_c_take_shared = c_take_shared;
113+
const auto alias_c_take_enum = c_take_enum;
114+
104115
} // namespace tests

tests/test.rs

+44
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,26 @@ fn test_c_return() {
7373
}
7474
}
7575

76+
#[test]
77+
fn test_alias_c_return() {
78+
let mut shared = ffi::Shared { z: 2020 };
79+
80+
assert_eq!(2020, alias::ffi::alias_c_return_shared().z);
81+
assert_eq!(2020, *alias::ffi::alias_c_return_ref(&shared));
82+
83+
{
84+
shared.z = 2019;
85+
let mut_z = alias::ffi::alias_c_return_mut(&mut shared);
86+
assert_eq!(2019, *mut_z);
87+
*mut_z = 2020;
88+
}
89+
90+
match alias::ffi::alias_c_return_enum(1) {
91+
enm @ ffi::Enum::BVal => assert_eq!(2020, enm.repr),
92+
_ => assert!(false),
93+
}
94+
}
95+
7696
#[test]
7797
fn test_c_try_return() {
7898
assert_eq!((), ffi::c_try_return_void().unwrap());
@@ -129,6 +149,15 @@ fn test_c_take() {
129149
check!(ffi::c_take_enum(ffi::Enum::AVal));
130150
}
131151

152+
#[test]
153+
fn test_alias_c_take() {
154+
check!(alias::ffi::alias_c_take_shared(ffi::Shared { z: 2020 }));
155+
check!(alias::ffi::alias_c_take_enum(ffi::Enum::AVal));
156+
check!(alias::ffi::alias_c_take_unique_ptr_vector_shared(
157+
alias::ffi::alias_c_return_unique_ptr_vector_shared()
158+
));
159+
}
160+
132161
/*
133162
// https://github.com/dtolnay/cxx/issues/232
134163
#[test]
@@ -159,6 +188,21 @@ fn test_c_call_r() {
159188
check!(cxx_run_test());
160189
}
161190

191+
#[test]
192+
fn test_alias_c_call_r() {
193+
fn cxx_run_alias_test() {
194+
extern "C" {
195+
fn cxx_run_alias_test() -> *const i8;
196+
}
197+
let failure = unsafe { cxx_run_alias_test() };
198+
if !failure.is_null() {
199+
let msg = unsafe { CStr::from_ptr(failure as *mut std::os::raw::c_char) };
200+
eprintln!("{}", msg.to_string_lossy());
201+
}
202+
}
203+
check!(cxx_run_alias_test());
204+
}
205+
162206
#[test]
163207
fn test_c_method_calls() {
164208
let mut unique_ptr = ffi::c_return_unique_ptr();

0 commit comments

Comments
 (0)