Skip to content

Commit 25b1ea0

Browse files
authored
Generate includes for tuples (#208)
This commit adds support for generating a `#include<~>` corresponding to each field in tuples. Here's an example. ```rust #[swift_bridge::bridge] mod ffi { extern "Rust" { fn some_function(arg1: (i32, bool)) -> (i32, bool); } } ``` Generates the following c header file: ```c #include <stdint.h> #include <stdbool.h> //... ```
1 parent 1b797f6 commit 25b1ea0

File tree

11 files changed

+148
-29
lines changed

11 files changed

+148
-29
lines changed

SwiftRustIntegrationTestRunner/SwiftRustIntegrationTestRunnerTests/TupleTests.swift

+7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ final class TupleTest: XCTestCase {
2727
XCTAssertEqual(tuple.1.toString(), "foo")
2828
XCTAssertEqual(tuple.2, 128)
2929
}
30+
XCTContext.runActivity(named: "Verify that we can pass and return a (F64, UInt, Bool).") {
31+
_ in
32+
let tuple = rust_reflect_tuple_f64_and_usize_and_bool((0.1, 123, true))
33+
XCTAssertEqual(tuple.0, 0.1)
34+
XCTAssertEqual(tuple.1, 123)
35+
XCTAssertEqual(tuple.2, true)
36+
}
3037
}
3138

3239
/// Verify that Rust can call Swift functions that accept and return Tuples.

crates/swift-bridge-ir/src/bridged_type.rs

+15-13
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ pub(crate) trait BridgeableType: Debug {
118118

119119
/// Generate a C include statement to put in the C header.
120120
/// For example, for a `u8` we would generate a `#include <stdint.h>` line.
121-
fn to_c_include(&self) -> Option<&'static str>;
121+
fn to_c_include(&self, types: &TypeDeclarations) -> Option<Vec<&'static str>>;
122122

123123
/// Get the FFI compatible Rust type.
124124
///
@@ -531,7 +531,7 @@ impl BridgeableType for BridgedType {
531531
self.to_c(types)
532532
}
533533

534-
fn to_c_include(&self) -> Option<&'static str> {
534+
fn to_c_include(&self, _types: &TypeDeclarations) -> Option<Vec<&'static str>> {
535535
todo!()
536536
}
537537

@@ -1645,9 +1645,9 @@ impl BridgedType {
16451645
}
16461646
}
16471647

1648-
pub fn to_c_include(&self) -> Option<&'static str> {
1648+
pub fn to_c_include(&self, types: &TypeDeclarations) -> Option<Vec<&'static str>> {
16491649
match self {
1650-
BridgedType::Bridgeable(b) => b.to_c_include(),
1650+
BridgedType::Bridgeable(b) => b.to_c_include(types),
16511651
BridgedType::StdLib(stdlib_type) => match stdlib_type {
16521652
StdLibType::U8
16531653
| StdLibType::I8
@@ -1658,18 +1658,15 @@ impl BridgedType {
16581658
| StdLibType::U64
16591659
| StdLibType::I64
16601660
| StdLibType::Usize
1661-
| StdLibType::Isize => Some("stdint.h"),
1662-
StdLibType::Bool => Some("stdbool.h"),
1661+
| StdLibType::Isize => Some(vec!["stdint.h"]),
1662+
StdLibType::Bool => Some(vec!["stdbool.h"]),
16631663
StdLibType::Pointer(ptr) => match &ptr.pointee {
1664-
Pointee::BuiltIn(ty) => ty.to_c_include(),
1664+
Pointee::BuiltIn(ty) => ty.to_c_include(types),
16651665
Pointee::Void(_) => None,
16661666
},
1667-
StdLibType::RefSlice(slice) => slice.ty.to_c_include(),
1668-
StdLibType::Vec(_vec) => Some("stdint.h"),
1669-
StdLibType::Tuple(_tuple) => {
1670-
// TODO: Iterate over the fields and see if any of them need imports..
1671-
None
1672-
}
1667+
StdLibType::RefSlice(slice) => slice.ty.to_c_include(types),
1668+
StdLibType::Vec(_vec) => Some(vec!["stdint.h"]),
1669+
StdLibType::Tuple(tuple) => tuple.to_c_include(types),
16731670
_ => None,
16741671
},
16751672
BridgedType::Foreign(CustomBridgedType::Shared(SharedType::Struct(_shared_struct))) => {
@@ -1855,9 +1852,14 @@ impl BridgedType {
18551852
StdLibType::U8 => "U8".to_string(),
18561853
StdLibType::U16 => "U16".to_string(),
18571854
StdLibType::U32 => "U32".to_string(),
1855+
StdLibType::Usize => "UInt".to_string(),
18581856
StdLibType::I8 => "I8".to_string(),
18591857
StdLibType::I16 => "I16".to_string(),
18601858
StdLibType::I32 => "I32".to_string(),
1859+
StdLibType::Isize => "Int".to_string(),
1860+
StdLibType::Bool => "Bool".to_string(),
1861+
StdLibType::F32 => "F32".to_string(),
1862+
StdLibType::F64 => "F64".to_string(),
18611863
_ => todo!(),
18621864
},
18631865
BridgedType::Foreign(ty) => match ty {

crates/swift-bridge-ir/src/bridged_type/bridgeable_pointer.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ impl BridgeableType for BuiltInPointer {
8181
todo!()
8282
}
8383

84-
fn to_c_include(&self) -> Option<&'static str> {
84+
fn to_c_include(&self, _types: &TypeDeclarations) -> Option<Vec<&'static str>> {
8585
todo!()
8686
}
8787

crates/swift-bridge-ir/src/bridged_type/bridgeable_string.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ impl BridgeableType for BridgedString {
6666
"void*".to_string()
6767
}
6868

69-
fn to_c_include(&self) -> Option<&'static str> {
69+
fn to_c_include(&self, _types: &TypeDeclarations) -> Option<Vec<&'static str>> {
7070
None
7171
}
7272

crates/swift-bridge-ir/src/bridged_type/bridged_opaque_type.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ impl BridgeableType for OpaqueForeignType {
145145
}
146146
}
147147

148-
fn to_c_include(&self) -> Option<&'static str> {
148+
fn to_c_include(&self, _types: &TypeDeclarations) -> Option<Vec<&'static str>> {
149149
None
150150
}
151151

crates/swift-bridge-ir/src/bridged_type/built_in_tuple.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ impl BridgeableType for BuiltInTuple {
136136
format!("struct {}", ty_name)
137137
}
138138

139-
fn to_c_include(&self) -> Option<&'static str> {
140-
todo!()
139+
fn to_c_include(&self, types: &TypeDeclarations) -> Option<Vec<&'static str>> {
140+
self.0.to_c_include(types)
141141
}
142142

143143
fn to_ffi_compatible_rust_type(

crates/swift-bridge-ir/src/bridged_type/shared_struct.rs

+16
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,22 @@ impl UnnamedStructFields {
207207
})
208208
.collect()
209209
}
210+
pub fn to_c_include(&self, types: &TypeDeclarations) -> Option<Vec<&'static str>> {
211+
let mut includes = vec![];
212+
for field in self.0.iter() {
213+
let ty = BridgedType::new_with_type(&field.ty, types).unwrap();
214+
if let Some(field_includes) = ty.to_c_include(types) {
215+
for include in field_includes {
216+
includes.push(include);
217+
}
218+
}
219+
}
220+
if includes.len() > 0 {
221+
Some(includes)
222+
} else {
223+
None
224+
}
225+
}
210226
}
211227

212228
#[derive(Clone)]

crates/swift-bridge-ir/src/codegen/codegen_tests/built_in_tuple_codegen_tests.rs

+79
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ public func some_function(_ arg: (Int32, UInt8)) -> (Int32, UInt8) {
4545
fn expected_c_header() -> ExpectedCHeader {
4646
ExpectedCHeader::ContainsManyAfterTrim(vec![
4747
r#"
48+
#include <stdint.h>
49+
"#,
50+
r#"
4851
typedef struct __swift_bridge__$tuple$I32U8 { int32_t _0; uint8_t _1; } __swift_bridge__$tuple$I32U8;
4952
"#,
5053
r#"
@@ -108,6 +111,9 @@ public func some_function<GenericIntoRustString: IntoRustString>(_ arg1: (Generi
108111
fn expected_c_header() -> ExpectedCHeader {
109112
ExpectedCHeader::ContainsManyAfterTrim(vec![
110113
r#"
114+
#include <stdint.h>
115+
"#,
116+
r#"
111117
typedef struct __swift_bridge__$tuple$StringU32 { void* _0; uint32_t _1; } __swift_bridge__$tuple$StringU32;
112118
"#,
113119
r#"
@@ -176,6 +182,9 @@ public func some_function(_ arg1: (SomeType, UInt32)) -> (SomeType, UInt32) {
176182
fn expected_c_header() -> ExpectedCHeader {
177183
ExpectedCHeader::ContainsManyAfterTrim(vec![
178184
r#"
185+
#include <stdint.h>
186+
"#,
187+
r#"
179188
typedef struct __swift_bridge__$tuple$SomeTypeU32 { void* _0; uint32_t _1; } __swift_bridge__$tuple$SomeTypeU32;
180189
"#,
181190
r#"
@@ -196,6 +205,76 @@ struct __swift_bridge__$tuple$SomeTypeU32 __swift_bridge__$some_function(struct
196205
}
197206
}
198207

208+
/// Verify that we can use a (f32, isize, bool) as Rust function arg and return type.
209+
mod extern_rust_tuple_f32_isize_bool {
210+
use super::*;
211+
212+
fn bridge_module_tokens() -> TokenStream {
213+
quote! {
214+
#[swift_bridge::bridge]
215+
mod ffi {
216+
extern "Rust" {
217+
fn some_function(arg1: (f32, isize, bool)) -> (f32, isize, bool);
218+
}
219+
}
220+
}
221+
}
222+
223+
fn expected_rust_tokens() -> ExpectedRustTokens {
224+
ExpectedRustTokens::ContainsMany(vec![
225+
quote! {
226+
pub extern "C" fn __swift_bridge__some_function (arg1: __swift_bridge__tuple_F32IntBool) -> __swift_bridge__tuple_F32IntBool {
227+
{ let val = super::some_function({let val = arg1; (val.0, val.1, val.2)});
228+
__swift_bridge__tuple_F32IntBool(val.0, val.1, val.2) }
229+
}
230+
},
231+
quote! {
232+
#[repr(C)]
233+
#[doc(hidden)]
234+
pub struct __swift_bridge__tuple_F32IntBool(f32, isize, bool);
235+
},
236+
])
237+
}
238+
239+
fn expected_swift_code() -> ExpectedSwiftCode {
240+
ExpectedSwiftCode::ContainsManyAfterTrim(vec![
241+
r#"
242+
public func some_function(_ arg1: (Float, Int, Bool)) -> (Float, Int, Bool) {
243+
{ let val = __swift_bridge__$some_function(__swift_bridge__$tuple$F32IntBool(_0: arg1.0, _1: arg1.1, _2: arg1.2)); return (val._0, val._1, val._2); }()
244+
}
245+
"#,
246+
])
247+
}
248+
249+
fn expected_c_header() -> ExpectedCHeader {
250+
ExpectedCHeader::ContainsManyAfterTrim(vec![
251+
r#"
252+
#include <stdint.h>
253+
"#,
254+
r#"
255+
#include <stdbool.h>
256+
"#,
257+
r#"
258+
typedef struct __swift_bridge__$tuple$F32IntBool { float _0; intptr_t _1; bool _2; } __swift_bridge__$tuple$F32IntBool;
259+
"#,
260+
r#"
261+
struct __swift_bridge__$tuple$F32IntBool __swift_bridge__$some_function(struct __swift_bridge__$tuple$F32IntBool arg1);
262+
"#,
263+
])
264+
}
265+
266+
#[test]
267+
fn extern_rust_tuple_f32_isize_bool() {
268+
CodegenTest {
269+
bridge_module: bridge_module_tokens().into(),
270+
expected_rust_tokens: expected_rust_tokens(),
271+
expected_swift_code: expected_swift_code(),
272+
expected_c_header: expected_c_header(),
273+
}
274+
.test();
275+
}
276+
}
277+
199278
/// Verify that we can use a (primitive type, primitive type) as Swift function arg and return type.
200279
mod extern_swift_tuple_primitives {
201280
use super::*;

crates/swift-bridge-ir/src/codegen/generate_c_header.rs

+16-8
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,10 @@ impl SwiftBridgeModule {
7171
for field in f.iter() {
7272
let ty = BridgedType::new_with_type(&field.ty, &self.types)
7373
.unwrap();
74-
if let Some(include) = ty.to_c_include() {
75-
bookkeeping.includes.insert(include);
74+
if let Some(includes) = ty.to_c_include(&self.types) {
75+
for include in includes {
76+
bookkeeping.includes.insert(include);
77+
}
7678
}
7779

7880
let name = field.swift_name_string();
@@ -84,8 +86,10 @@ impl SwiftBridgeModule {
8486
for (idx, field) in types.iter().enumerate() {
8587
let ty = BridgedType::new_with_type(&field.ty, &self.types)
8688
.unwrap();
87-
if let Some(include) = ty.to_c_include() {
88-
bookkeeping.includes.insert(include);
89+
if let Some(includes) = ty.to_c_include(&self.types) {
90+
for include in includes {
91+
bookkeeping.includes.insert(include);
92+
}
8993
}
9094

9195
let name = format!("_{}", idx);
@@ -177,8 +181,10 @@ typedef struct {option_ffi_name} {{ bool is_some; {ffi_name} val; }} {option_ffi
177181
&self.types,
178182
)
179183
.unwrap();
180-
if let Some(include) = ty.to_c_include() {
181-
bookkeeping.includes.insert(include);
184+
if let Some(includes) = ty.to_c_include(&self.types) {
185+
for include in includes {
186+
bookkeeping.includes.insert(include);
187+
}
182188
}
183189
let ty = ty.to_c(&self.types);
184190
let field_name = named_field.name.to_string();
@@ -197,8 +203,10 @@ typedef struct {option_ffi_name} {{ bool is_some; {ffi_name} val; }} {option_ffi
197203
&self.types,
198204
)
199205
.unwrap();
200-
if let Some(include) = ty.to_c_include() {
201-
bookkeeping.includes.insert(include);
206+
if let Some(includes) = ty.to_c_include(&self.types) {
207+
for include in includes {
208+
bookkeeping.includes.insert(include);
209+
}
202210
}
203211
let ty = ty.to_c(&self.types);
204212
params.push(format!("{} _{};", ty, unnamed_field.idx));

crates/swift-bridge-ir/src/parsed_extern_fn.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ impl ParsedExternFn {
401401

402402
if let ReturnType::Type(_, ty) = &self.func.sig.output {
403403
if let Some(ty) = BridgedType::new_with_type(&ty, types) {
404-
if let Some(include) = ty.to_c_include() {
404+
if let Some(include) = ty.to_c_include(types) {
405405
includes.push(include);
406406
}
407407
}
@@ -410,15 +410,15 @@ impl ParsedExternFn {
410410
for param in &self.func.sig.inputs {
411411
if let FnArg::Typed(pat_ty) = param {
412412
if let Some(ty) = BridgedType::new_with_type(&pat_ty.ty, types) {
413-
if let Some(include) = ty.to_c_include() {
413+
if let Some(include) = ty.to_c_include(types) {
414414
includes.push(include);
415415
}
416416
}
417417
}
418418
}
419419

420420
if includes.len() > 0 {
421-
Some(includes)
421+
Some(includes.into_iter().flatten().collect())
422422
} else {
423423
None
424424
}

crates/swift-integration-tests/src/tuple.rs

+7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ mod ffi {
99
fn rust_reflect_tuple_opaque_rust_and_string_and_primitive(
1010
tuple: (TupleTestOpaqueRustType, String, u8),
1111
) -> (TupleTestOpaqueRustType, String, u8);
12+
fn rust_reflect_tuple_f64_and_usize_and_bool(
13+
tuple: (f64, usize, bool),
14+
) -> (f64, usize, bool);
1215
}
1316
extern "Swift" {
1417
fn swift_reflect_tuple_primitives(arg: (i32, u32)) -> (i32, u32);
@@ -39,6 +42,10 @@ fn rust_reflect_tuple_opaque_rust_and_string_and_primitive(
3942
tuple
4043
}
4144

45+
fn rust_reflect_tuple_f64_and_usize_and_bool(tuple: (f64, usize, bool)) -> (f64, usize, bool) {
46+
tuple
47+
}
48+
4249
fn test_rust_calls_swift_tuples() {
4350
let val = ffi::swift_reflect_tuple_primitives((-123, 123));
4451
assert_eq!(val.0, -123);

0 commit comments

Comments
 (0)