Skip to content

Commit 909b8fc

Browse files
authored
Merge pull request #503 from dtolnay/array
Implement Rust [T; N] <-> std::array<T, N>
2 parents f5ead10 + 1092640 commit 909b8fc

18 files changed

+186
-13
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ returns of functions.
327327
<tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
328328
<tr><td>Box&lt;T&gt;</td><td>rust::Box&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
329329
<tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.UniquePtr.html">UniquePtr&lt;T&gt;</a></td><td>std::unique_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
330+
<tr><td>[T; N]</td><td>std::array&lt;T, N&gt;</td><td><sup><i>cannot hold opaque Rust or C++ type</i></sup></td></tr>
330331
<tr><td>Vec&lt;T&gt;</td><td>rust::Vec&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
331332
<tr><td><a href="https://docs.rs/cxx/1.0/cxx/struct.CxxVector.html">CxxVector&lt;T&gt;</a></td><td>std::vector&lt;T&gt;</td><td><sup><i>cannot be passed by value, cannot hold opaque Rust type</i></sup></td></tr>
332333
<tr><td>fn(T, U) -&gt; V</td><td>rust::Fn&lt;V(T, U)&gt;</td><td><sup><i>only passing from Rust to C++ is implemented so far</i></sup></td></tr>

book/src/bindings.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ returns of extern functions.
1616
<tr><td style="padding:3px 6px"><b><a href="binding/cxxstring.md">CxxString</a></b></td><td style="padding:3px 6px">std::string</td><td style="padding:3px 6px"><sup><i>cannot be passed by value</i></sup></td></tr>
1717
<tr><td style="padding:3px 6px">Box&lt;T&gt;</td><td style="padding:3px 6px"><b><a href="binding/box.md">rust::Box&lt;T&gt;</a></b></td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
1818
<tr><td style="padding:3px 6px"><b><a href="binding/uniqueptr.md">UniquePtr&lt;T&gt;</a></b></td><td style="padding:3px 6px">std::unique_ptr&lt;T&gt;</td><td style="padding:3px 6px"><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
19+
<tr><td style="padding:3px 6px">[T; N]</td><td style="padding:3px 6px">std::array&lt;T, N&gt;</td><td style="padding:3px 6px"><sup><i>cannot hold opaque Rust or C++ type</i></sup></td></tr>
1920
<tr><td style="padding:3px 6px">Vec&lt;T&gt;</td><td style="padding:3px 6px"><b><a href="binding/vec.md">rust::Vec&lt;T&gt;</a></b></td><td style="padding:3px 6px"><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
2021
<tr><td style="padding:3px 6px"><b><a href="binding/cxxvector.md">CxxVector&lt;T&gt;</a></b></td><td style="padding:3px 6px">std::vector&lt;T&gt;</td><td style="padding:3px 6px"><sup><i>cannot be passed by value, cannot hold opaque Rust type</i></sup></td></tr>
2122
<tr><td style="padding:3px 6px">fn(T, U) -&gt; V</td><td style="padding:3px 6px"><b><a href="binding/fn.md">rust::Fn&lt;V(T, U)&gt;</a></b></td><td style="padding:3px 6px"><sup><i>only passing from Rust to C++ is implemented so far</i></sup></td></tr>

gen/src/write.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) {
172172
out.include.cstdint = true;
173173
out.builtin.rust_slice = true;
174174
}
175+
Type::Array(_) => out.include.array = true,
175176
Type::Ref(_) | Type::Void(_) => {}
176177
}
177178
}
@@ -919,6 +920,11 @@ fn write_type(out: &mut OutFile, ty: &Type) {
919920
}
920921
write!(out, ")>");
921922
}
923+
Type::Array(a) => {
924+
write!(out, "::std::array<");
925+
write_type(out, &a.inner);
926+
write!(out, ", {}>", &a.len);
927+
}
922928
Type::Void(_) => unreachable!(),
923929
}
924930
}
@@ -957,7 +963,8 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) {
957963
| Type::CxxVector(_)
958964
| Type::RustVec(_)
959965
| Type::SliceRefU8(_)
960-
| Type::Fn(_) => write!(out, " "),
966+
| Type::Fn(_)
967+
| Type::Array(_) => write!(out, " "),
961968
Type::Ref(_) => {}
962969
Type::Void(_) | Type::Slice(_) => unreachable!(),
963970
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@
336336
//! <tr><td><a href="struct.CxxString.html">CxxString</a></td><td>std::string</td><td><sup><i>cannot be passed by value</i></sup></td></tr>
337337
//! <tr><td>Box&lt;T&gt;</td><td>rust::Box&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
338338
//! <tr><td><a href="struct.UniquePtr.html">UniquePtr&lt;T&gt;</a></td><td>std::unique_ptr&lt;T&gt;</td><td><sup><i>cannot hold opaque Rust type</i></sup></td></tr>
339+
//! <tr><td>[T; N]</td><td>std::array&lt;T, N&gt;</td><td><sup><i>cannot hold opaque Rust or C++ type</i></sup></td></tr>
339340
//! <tr><td>Vec&lt;T&gt;</td><td>rust::Vec&lt;T&gt;</td><td><sup><i>cannot hold opaque C++ type</i></sup></td></tr>
340341
//! <tr><td><a href="struct.CxxVector.html">CxxVector&lt;T&gt;</a></td><td>std::vector&lt;T&gt;</td><td><sup><i>cannot be passed by value, cannot hold opaque Rust type</i></sup></td></tr>
341342
//! <tr><td>fn(T, U) -&gt; V</td><td>rust::Fn&lt;V(T, U)&gt;</td><td><sup><i>only passing from Rust to C++ is implemented so far</i></sup></td></tr>

syntax/check.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use crate::syntax::atom::Atom::{self, *};
22
use crate::syntax::report::Errors;
33
use crate::syntax::types::TrivialReason;
44
use crate::syntax::{
5-
error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Signature, Slice,
6-
Struct, Ty1, Type, Types,
5+
error, ident, Api, Array, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Signature,
6+
Slice, Struct, Ty1, Type, Types,
77
};
88
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
99
use quote::{quote, ToTokens};
@@ -35,6 +35,7 @@ fn do_typecheck(cx: &mut Check) {
3535
Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr),
3636
Type::Ref(ty) => check_type_ref(cx, ty),
3737
Type::Slice(ty) => check_type_slice(cx, ty),
38+
Type::Array(array) => check_type_array(cx, array),
3839
Type::Fn(ty) => check_type_fn(cx, ty),
3940
Type::Str(_) | Type::Void(_) | Type::SliceRefU8(_) => {}
4041
}
@@ -184,6 +185,17 @@ fn check_type_slice(cx: &mut Check, ty: &Slice) {
184185
);
185186
}
186187

188+
fn check_type_array(cx: &mut Check, ty: &Array) {
189+
let supported = match &ty.inner {
190+
Type::Fn(_) => false,
191+
element => !is_unsized(cx, element),
192+
};
193+
194+
if !supported {
195+
cx.error(ty, "unsupported array element type");
196+
}
197+
}
198+
187199
fn check_type_fn(cx: &mut Check, ty: &Signature) {
188200
if ty.throws {
189201
cx.error(ty, "function pointer returning Result is not supported yet");
@@ -414,6 +426,7 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
414426
let ident = &ident.rust;
415427
ident == CxxString || is_opaque_cxx(cx, ident) || cx.types.rust.contains(ident)
416428
}
429+
Type::Array(array) => is_unsized(cx, &array.inner),
417430
Type::CxxVector(_) | Type::Slice(_) | Type::Void(_) => true,
418431
Type::RustBox(_)
419432
| Type::RustVec(_)
@@ -498,5 +511,6 @@ fn describe(cx: &mut Check, ty: &Type) -> String {
498511
},
499512
Type::Fn(_) => "function pointer".to_owned(),
500513
Type::Void(_) => "()".to_owned(),
514+
Type::Array(_) => "array".to_owned(),
501515
}
502516
}

syntax/impls.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::syntax::{ExternFn, Impl, Include, Receiver, Ref, Signature, Slice, Ty1, Type};
1+
use crate::syntax::{Array, ExternFn, Impl, Include, Receiver, Ref, Signature, Slice, Ty1, Type};
22
use std::borrow::Borrow;
33
use std::hash::{Hash, Hasher};
44
use std::mem;
@@ -50,6 +50,7 @@ impl Hash for Type {
5050
Type::Fn(t) => t.hash(state),
5151
Type::Slice(t) => t.hash(state),
5252
Type::SliceRefU8(t) => t.hash(state),
53+
Type::Array(t) => t.hash(state),
5354
Type::Void(_) => {}
5455
}
5556
}
@@ -173,6 +174,42 @@ impl Hash for Slice {
173174
}
174175
}
175176

177+
impl Eq for Array {}
178+
179+
impl PartialEq for Array {
180+
fn eq(&self, other: &Array) -> bool {
181+
let Array {
182+
bracket: _,
183+
inner,
184+
semi_token: _,
185+
len,
186+
len_token: _,
187+
} = self;
188+
let Array {
189+
bracket: _,
190+
inner: inner2,
191+
semi_token: _,
192+
len: len2,
193+
len_token: _,
194+
} = other;
195+
inner == inner2 && len == len2
196+
}
197+
}
198+
199+
impl Hash for Array {
200+
fn hash<H: Hasher>(&self, state: &mut H) {
201+
let Array {
202+
bracket: _,
203+
inner,
204+
semi_token: _,
205+
len,
206+
len_token: _,
207+
} = self;
208+
inner.hash(state);
209+
len.hash(state);
210+
}
211+
}
212+
176213
impl Eq for Signature {}
177214

178215
impl PartialEq for Signature {

syntax/improper.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ impl<'a> Types<'a> {
3131
| Type::SliceRefU8(_) => Definite(true),
3232
Type::UniquePtr(_) | Type::CxxVector(_) => Definite(false),
3333
Type::Ref(ty) => self.determine_improper_ctype(&ty.inner),
34+
Type::Array(ty) => self.determine_improper_ctype(&ty.inner),
3435
}
3536
}
3637
}

syntax/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use self::symbol::Symbol;
3030
use proc_macro2::{Ident, Span};
3131
use syn::punctuated::Punctuated;
3232
use syn::token::{Brace, Bracket, Paren};
33-
use syn::{Expr, Generics, Lifetime, Token, Type as RustType};
33+
use syn::{Expr, Generics, Lifetime, LitInt, Token, Type as RustType};
3434

3535
pub use self::atom::Atom;
3636
pub use self::derive::Derive;
@@ -166,6 +166,7 @@ pub enum Type {
166166
Void(Span),
167167
Slice(Box<Slice>),
168168
SliceRefU8(Box<Ref>),
169+
Array(Box<Array>),
169170
}
170171

171172
pub struct Ty1 {
@@ -190,6 +191,14 @@ pub struct Slice {
190191
pub inner: Type,
191192
}
192193

194+
pub struct Array {
195+
pub bracket: Bracket,
196+
pub inner: Type,
197+
pub semi_token: Token![;],
198+
pub len: usize,
199+
pub len_token: LitInt,
200+
}
201+
193202
#[derive(Copy, Clone, PartialEq)]
194203
pub enum Lang {
195204
Cxx,

syntax/parse.rs

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::syntax::file::{Item, ItemForeignMod};
33
use crate::syntax::report::Errors;
44
use crate::syntax::Atom::*;
55
use crate::syntax::{
6-
attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang,
6+
attrs, error, Api, Array, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, Lang,
77
Namespace, Pair, Receiver, Ref, ResolvableName, Signature, Slice, Struct, Ty1, Type, TypeAlias,
88
Var, Variant,
99
};
@@ -12,10 +12,10 @@ use quote::{format_ident, quote, quote_spanned};
1212
use syn::parse::{ParseStream, Parser};
1313
use syn::punctuated::Punctuated;
1414
use syn::{
15-
Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
16-
GenericArgument, GenericParam, Generics, Ident, ItemEnum, ItemImpl, ItemStruct, LitStr, Pat,
17-
PathArguments, Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath,
18-
TypeReference, TypeSlice,
15+
Abi, Attribute, Error, Expr, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
16+
GenericArgument, GenericParam, Generics, Ident, ItemEnum, ItemImpl, ItemStruct, Lit, LitStr,
17+
Pat, PathArguments, Result, ReturnType, Token, Type as RustType, TypeArray, TypeBareFn,
18+
TypePath, TypeReference, TypeSlice,
1919
};
2020

2121
pub mod kw {
@@ -639,6 +639,7 @@ fn parse_type(ty: &RustType, namespace: &Namespace) -> Result<Type> {
639639
RustType::Reference(ty) => parse_type_reference(ty, namespace),
640640
RustType::Path(ty) => parse_type_path(ty, namespace),
641641
RustType::Slice(ty) => parse_type_slice(ty, namespace),
642+
RustType::Array(ty) => parse_type_array(ty, namespace),
642643
RustType::BareFn(ty) => parse_type_fn(ty, namespace),
643644
RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)),
644645
_ => Err(Error::new_spanned(ty, "unsupported type")),
@@ -747,6 +748,36 @@ fn parse_type_slice(ty: &TypeSlice, namespace: &Namespace) -> Result<Type> {
747748
})))
748749
}
749750

751+
fn parse_type_array(ty: &TypeArray, namespace: &Namespace) -> Result<Type> {
752+
let inner = parse_type(&ty.elem, namespace)?;
753+
754+
let len_expr = if let Expr::Lit(lit) = &ty.len {
755+
lit
756+
} else {
757+
let msg = "unsupported expression, array length must be an integer literal";
758+
return Err(Error::new_spanned(&ty.len, msg));
759+
};
760+
761+
let len_token = if let Lit::Int(int) = &len_expr.lit {
762+
int.clone()
763+
} else {
764+
let msg = "array length must be an integer literal";
765+
return Err(Error::new_spanned(len_expr, msg));
766+
};
767+
768+
let bracket = ty.bracket_token;
769+
let semi_token = ty.semi_token;
770+
let len = len_token.base10_parse::<usize>()?;
771+
772+
Ok(Type::Array(Box::new(Array {
773+
bracket,
774+
inner,
775+
semi_token,
776+
len,
777+
len_token,
778+
})))
779+
}
780+
750781
fn parse_type_fn(ty: &TypeBareFn, namespace: &Namespace) -> Result<Type> {
751782
if ty.lifetimes.is_some() {
752783
return Err(Error::new_spanned(

syntax/tokens.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::syntax::atom::Atom::*;
22
use crate::syntax::{
3-
Atom, Derive, Enum, ExternFn, ExternType, Impl, Receiver, Ref, ResolvableName, Signature,
4-
Slice, Struct, Ty1, Type, TypeAlias, Var,
3+
Array, Atom, Derive, Enum, ExternFn, ExternType, Impl, Receiver, Ref, ResolvableName,
4+
Signature, Slice, Struct, Ty1, Type, TypeAlias, Var,
55
};
66
use proc_macro2::{Ident, Span, TokenStream};
77
use quote::{quote_spanned, ToTokens};
@@ -22,6 +22,7 @@ impl ToTokens for Type {
2222
}
2323
Type::Ref(r) | Type::Str(r) | Type::SliceRefU8(r) => r.to_tokens(tokens),
2424
Type::Slice(s) => s.to_tokens(tokens),
25+
Type::Array(a) => a.to_tokens(tokens),
2526
Type::Fn(f) => f.to_tokens(tokens),
2627
Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())),
2728
}
@@ -68,6 +69,16 @@ impl ToTokens for Ref {
6869
}
6970
}
7071

72+
impl ToTokens for Array {
73+
fn to_tokens(&self, tokens: &mut TokenStream) {
74+
self.bracket.surround(tokens, |tokens| {
75+
self.inner.to_tokens(tokens);
76+
self.semi_token.to_tokens(tokens);
77+
self.len_token.to_tokens(tokens);
78+
});
79+
}
80+
}
81+
7182
impl ToTokens for Slice {
7283
fn to_tokens(&self, tokens: &mut TokenStream) {
7384
self.bracket.surround(tokens, |tokens| {

syntax/types.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ impl<'a> Types<'a> {
4949
| Type::RustVec(ty) => visit(all, &ty.inner),
5050
Type::Ref(r) => visit(all, &r.inner),
5151
Type::Slice(s) => visit(all, &s.inner),
52+
Type::Array(a) => visit(all, &a.inner),
5253
Type::Fn(f) => {
5354
if let Some(ret) = &f.ret {
5455
visit(all, ret);
@@ -253,7 +254,7 @@ impl<'a> Types<'a> {
253254
Atom::from(&ident.rust) == Some(RustString)
254255
}
255256
}
256-
Type::RustVec(_) => true,
257+
Type::RustVec(_) | Type::Array(_) => true,
257258
_ => false,
258259
}
259260
}

tests/ffi/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ pub mod ffi {
6565
e: COwnedEnum,
6666
}
6767

68+
pub struct Array {
69+
a: [i32; 4],
70+
}
71+
6872
unsafe extern "C++" {
6973
include!("tests/ffi/tests.h");
7074

@@ -155,6 +159,7 @@ pub mod ffi {
155159
fn set_succeed(self: Pin<&mut C>, n: usize) -> Result<usize>;
156160
fn get_fail(self: Pin<&mut C>) -> Result<usize>;
157161
fn c_method_on_shared(self: &Shared) -> usize;
162+
fn c_set_array(self: &mut Array, value: i32);
158163

159164
#[rust_name = "i32_overloaded_method"]
160165
fn cOverloadedMethod(&self, x: i32) -> String;
@@ -229,6 +234,7 @@ pub mod ffi {
229234
fn get(self: &R) -> usize;
230235
fn set(self: &mut R, n: usize) -> usize;
231236
fn r_method_on_shared(self: &Shared) -> String;
237+
fn r_get_array_sum(self: &Array) -> i32;
232238

233239
#[cxx_name = "rAliasedFunction"]
234240
fn r_aliased_function(x: i32) -> String;
@@ -328,6 +334,12 @@ impl ffi::Shared {
328334
}
329335
}
330336

337+
impl ffi::Array {
338+
pub fn r_get_array_sum(&self) -> i32 {
339+
self.a.iter().sum()
340+
}
341+
}
342+
331343
#[derive(Debug)]
332344
struct Error;
333345

tests/ffi/tests.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ size_t C::get_fail() { throw std::runtime_error("unimplemented"); }
3232

3333
size_t Shared::c_method_on_shared() const noexcept { return 2021; }
3434

35+
void Array::c_set_array(int32_t val) noexcept {
36+
this->a = {val, val, val, val};
37+
}
38+
3539
const std::vector<uint8_t> &C::get_v() const { return this->v; }
3640

3741
std::vector<uint8_t> &C::get_v() { return this->v; }

tests/test.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,11 @@ fn test_c_method_calls() {
198198
assert_eq!(2022, unique_ptr.pin_mut().set_succeed(2022).unwrap(),);
199199
assert!(unique_ptr.pin_mut().get_fail().is_err());
200200
assert_eq!(2021, ffi::Shared { z: 0 }.c_method_on_shared());
201+
202+
let val = 42;
203+
let mut array = ffi::Array { a: [0, 0, 0, 0] };
204+
array.c_set_array(val);
205+
assert_eq!(array.a.len() as i32 * val, array.r_get_array_sum());
201206
}
202207

203208
#[test]

tests/ui/array_len_expr.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#[cxx::bridge]
2+
mod ffi {
3+
unsafe extern "C++" {
4+
fn arraystr() -> [String; "13"];
5+
fn arraysub() -> [String; 15 - 1];
6+
}
7+
}
8+
9+
fn main() {}

0 commit comments

Comments
 (0)