Skip to content

Commit 43dacd0

Browse files
committed
[workerd-cxx] require lifetime annotations on all returned references (#3)
To support error propagation cxx generates C interface where successful result value is passed out as a pointer. This requires moving result type to parameter position which changes the meaning according to the rules of implicit liftime (each parameter gets its own lifetime). Rather than implement the same set of rules that compiler to generate such annotations, demand the user to always have explicit lifetimes for returned references. This should not be a great deal in practice with `unsafe` being the only inconvenience. In return the follow up no-abort work gets much simpler. I might revisit this change in the future if the burden of explicit lifetimes becomes too high. [workerd-cxx] fixing build (#6)
1 parent 146e2cc commit 43dacd0

File tree

12 files changed

+103
-63
lines changed

12 files changed

+103
-63
lines changed

demo/src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ mod ffi {
1010
extern "Rust" {
1111
type MultiBuf;
1212

13-
fn next_chunk(buf: &mut MultiBuf) -> &[u8];
13+
#[allow(clippy::needless_lifetimes)]
14+
unsafe fn next_chunk<'a>(buf: &'a mut MultiBuf) -> &'a [u8];
1415
}
1516

1617
// C++ types and signatures exposed to Rust.

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
//! type MultiBuf;
9191
//!
9292
//! // Functions implemented in Rust.
93-
//! fn next_chunk(buf: &mut MultiBuf) -> &[u8];
93+
//! unsafe fn next_chunk<'a>(buf: &'a mut MultiBuf) -> &'a [u8];
9494
//! }
9595
//!
9696
//! unsafe extern "C++" {

syntax/parse.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1461,7 +1461,31 @@ fn parse_return_type(
14611461

14621462
match parse_type(ret)? {
14631463
Type::Void(_) => Ok(None),
1464-
ty => Ok(Some(ty)),
1464+
ty => {
1465+
if has_references_without_lifetime(&ty) {
1466+
return Err(Error::new_spanned(
1467+
ret,
1468+
"cxx bridge requires all return type references to have explicit lifetimes",
1469+
));
1470+
}
1471+
Ok(Some(ty))
1472+
}
1473+
}
1474+
}
1475+
1476+
fn has_references_without_lifetime(ty: &Type) -> bool {
1477+
match ty {
1478+
Type::Fn(_) | Type::Ident(_) | Type::Str(_) | Type::Void(_) => false,
1479+
Type::RustBox(t)
1480+
| Type::RustVec(t)
1481+
| Type::UniquePtr(t)
1482+
| Type::SharedPtr(t)
1483+
| Type::WeakPtr(t)
1484+
| Type::CxxVector(t) => has_references_without_lifetime(&t.inner),
1485+
Type::Ptr(t) => has_references_without_lifetime(&t.inner),
1486+
Type::Array(t) => has_references_without_lifetime(&t.inner),
1487+
Type::SliceRef(t) => t.lifetime.is_none(),
1488+
Type::Ref(t) => t.lifetime.is_none(),
14651489
}
14661490
}
14671491

tests/ffi/lib.rs

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,11 @@ pub mod ffi {
102102
fn c_return_box() -> Box<R>;
103103
fn c_return_unique_ptr() -> UniquePtr<C>;
104104
fn c_return_shared_ptr() -> SharedPtr<C>;
105-
fn c_return_ref(shared: &Shared) -> &usize;
106-
fn c_return_mut(shared: &mut Shared) -> &mut usize;
107-
fn c_return_str(shared: &Shared) -> &str;
108-
fn c_return_slice_char(shared: &Shared) -> &[c_char];
109-
fn c_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8];
105+
unsafe fn c_return_mut<'a>(shared: &'a mut Shared) -> &'a mut usize;
106+
unsafe fn c_return_str<'a>(shared: &'a Shared) -> &'a str;
107+
unsafe fn c_return_slice_char<'a>(shared: &'a Shared) -> &'a [c_char];
108+
unsafe fn c_return_mutsliceu8<'a>(slice: &'a mut [u8]) -> &'a mut [u8];
109+
unsafe fn c_return_ref<'a>(shared: &'a Shared) -> &'a usize;
110110
fn c_return_rust_string() -> String;
111111
fn c_return_rust_string_lossy() -> String;
112112
fn c_return_unique_ptr_string() -> UniquePtr<CxxString>;
@@ -115,18 +115,18 @@ pub mod ffi {
115115
fn c_return_unique_ptr_vector_string() -> UniquePtr<CxxVector<CxxString>>;
116116
fn c_return_unique_ptr_vector_shared() -> UniquePtr<CxxVector<Shared>>;
117117
fn c_return_unique_ptr_vector_opaque() -> UniquePtr<CxxVector<C>>;
118-
fn c_return_ref_vector(c: &C) -> &CxxVector<u8>;
119-
fn c_return_mut_vector(c: Pin<&mut C>) -> Pin<&mut CxxVector<u8>>;
118+
unsafe fn c_return_ref_vector<'a>(c: &'a C) -> &'a CxxVector<u8>;
119+
unsafe fn c_return_mut_vector<'a>(c: Pin<&'a mut C>) -> Pin<&'a mut CxxVector<u8>>;
120120
fn c_return_rust_vec_u8() -> Vec<u8>;
121-
fn c_return_ref_rust_vec(c: &C) -> &Vec<u8>;
122-
fn c_return_mut_rust_vec(c: Pin<&mut C>) -> &mut Vec<u8>;
121+
unsafe fn c_return_ref_rust_vec<'a>(c: &'a C) -> &'a Vec<u8>;
122+
unsafe fn c_return_mut_rust_vec<'a>(c: Pin<&'a mut C>) -> &'a mut Vec<u8>;
123123
fn c_return_rust_vec_string() -> Vec<String>;
124124
fn c_return_rust_vec_bool() -> Vec<bool>;
125125
fn c_return_identity(_: usize) -> usize;
126126
fn c_return_sum(_: usize, _: usize) -> usize;
127127
fn c_return_enum(n: u16) -> Enum;
128-
fn c_return_ns_ref(shared: &AShared) -> &usize;
129-
fn c_return_nested_ns_ref(shared: &ABShared) -> &usize;
128+
unsafe fn c_return_ns_ref<'a>(shared: &'a AShared) -> &'a usize;
129+
unsafe fn c_return_nested_ns_ref<'a>(shared: &'a ABShared) -> &'a usize;
130130
fn c_return_ns_enum(n: u16) -> AEnum;
131131
fn c_return_nested_ns_enum(n: u16) -> ABEnum;
132132
fn c_return_const_ptr(n: usize) -> *const C;
@@ -164,7 +164,7 @@ pub mod ffi {
164164
fn c_take_ref_rust_vec_string(v: &Vec<String>);
165165
fn c_take_ref_rust_vec_index(v: &Vec<u8>);
166166
fn c_take_ref_rust_vec_copy(v: &Vec<u8>);
167-
fn c_take_ref_shared_string(s: &SharedString) -> &SharedString;
167+
unsafe fn c_take_ref_shared_string<'a>(s: &'a SharedString) -> &'a SharedString;
168168
fn c_take_callback(callback: fn(String) -> usize);
169169
fn c_take_callback_ref(callback: fn(&String));
170170
#[cxx_name = "c_take_callback_ref"]
@@ -184,26 +184,26 @@ pub mod ffi {
184184
fn c_try_return_primitive() -> Result<usize>;
185185
fn c_fail_return_primitive() -> Result<usize>;
186186
fn c_try_return_box() -> Result<Box<R>>;
187-
fn c_try_return_ref(s: &String) -> Result<&String>;
188-
fn c_try_return_str(s: &str) -> Result<&str>;
189-
fn c_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>;
190-
fn c_try_return_mutsliceu8(s: &mut [u8]) -> Result<&mut [u8]>;
187+
unsafe fn c_try_return_ref<'a>(s: &'a String) -> Result<&'a String>;
188+
unsafe fn c_try_return_str<'a>(s: &'a str) -> Result<&'a str>;
189+
unsafe fn c_try_return_sliceu8<'a>(s: &'a [u8]) -> Result<&'a [u8]>;
190+
unsafe fn c_try_return_mutsliceu8<'a>(s: &'a mut [u8]) -> Result<&'a mut [u8]>;
191191
fn c_try_return_rust_string() -> Result<String>;
192192
fn c_try_return_unique_ptr_string() -> Result<UniquePtr<CxxString>>;
193193
fn c_try_return_rust_vec() -> Result<Vec<u8>>;
194194
fn c_try_return_rust_vec_string() -> Result<Vec<String>>;
195-
fn c_try_return_ref_rust_vec(c: &C) -> Result<&Vec<u8>>;
195+
unsafe fn c_try_return_ref_rust_vec<'a>(c: &'a C) -> Result<&'a Vec<u8>>;
196196

197197
fn get(self: &C) -> usize;
198198
fn set(self: Pin<&mut C>, n: usize) -> usize;
199199
fn get2(&self) -> usize;
200-
fn getRef(self: &C) -> &usize;
201-
fn getMut(self: Pin<&mut C>) -> &mut usize;
200+
unsafe fn getRef<'a>(self: &'a C) -> &'a usize;
201+
unsafe fn getMut<'a>(self: Pin<&'a mut C>) -> &'a mut usize;
202202
fn set_succeed(self: Pin<&mut C>, n: usize) -> Result<usize>;
203203
fn get_fail(self: Pin<&mut C>) -> Result<usize>;
204204
fn c_method_on_shared(self: &Shared) -> usize;
205-
fn c_method_ref_on_shared(self: &Shared) -> &usize;
206-
fn c_method_mut_on_shared(self: &mut Shared) -> &mut usize;
205+
unsafe fn c_method_ref_on_shared<'a>(self: &'a Shared) -> &'a usize;
206+
unsafe fn c_method_mut_on_shared<'a>(self: &'a mut Shared) -> &'a mut usize;
207207
fn c_set_array(self: &mut Array, value: i32);
208208

209209
fn c_get_use_count(weak: &WeakPtr<C>) -> usize;
@@ -258,6 +258,7 @@ pub mod ffi {
258258
type Buffer = crate::Buffer;
259259
}
260260

261+
#[allow(clippy::extra_unused_lifetimes)]
261262
extern "Rust" {
262263
type R;
263264

@@ -266,19 +267,19 @@ pub mod ffi {
266267
fn r_return_box() -> Box<R>;
267268
fn r_return_unique_ptr() -> UniquePtr<C>;
268269
fn r_return_shared_ptr() -> SharedPtr<C>;
269-
fn r_return_ref(shared: &Shared) -> &usize;
270-
fn r_return_mut(shared: &mut Shared) -> &mut usize;
271-
fn r_return_str(shared: &Shared) -> &str;
270+
unsafe fn r_return_ref<'a>(shared: &'a Shared) -> &'a usize;
271+
unsafe fn r_return_mut<'a>(shared: &'a mut Shared) -> &'a mut usize;
272+
unsafe fn r_return_str<'a>(shared: &'a Shared) -> &'a str;
272273
unsafe fn r_return_str_via_out_param<'a>(shared: &'a Shared, out_param: &mut &'a str);
273-
fn r_return_sliceu8(shared: &Shared) -> &[u8];
274-
fn r_return_mutsliceu8(slice: &mut [u8]) -> &mut [u8];
274+
unsafe fn r_return_sliceu8<'a>(shared: &'a Shared) -> &'a [u8];
275+
unsafe fn r_return_mutsliceu8<'a>(slice: &'a mut [u8]) -> &'a mut [u8];
275276
fn r_return_rust_string() -> String;
276277
fn r_return_unique_ptr_string() -> UniquePtr<CxxString>;
277278
fn r_return_rust_vec() -> Vec<u8>;
278279
fn r_return_rust_vec_string() -> Vec<String>;
279280
fn r_return_rust_vec_extern_struct() -> Vec<Job>;
280-
fn r_return_ref_rust_vec(shared: &Shared) -> &Vec<u8>;
281-
fn r_return_mut_rust_vec(shared: &mut Shared) -> &mut Vec<u8>;
281+
unsafe fn r_return_ref_rust_vec<'a>(shared: &'a Shared) -> &'a Vec<u8>;
282+
unsafe fn r_return_mut_rust_vec<'a>(shared: &'a mut Shared) -> &'a mut Vec<u8>;
282283
fn r_return_identity(_: usize) -> usize;
283284
fn r_return_sum(_: usize, _: usize) -> usize;
284285
fn r_return_enum(n: u32) -> Enum;
@@ -306,8 +307,8 @@ pub mod ffi {
306307
fn r_try_return_primitive() -> Result<usize>;
307308
fn r_try_return_box() -> Result<Box<R>>;
308309
fn r_fail_return_primitive() -> Result<usize>;
309-
fn r_try_return_sliceu8(s: &[u8]) -> Result<&[u8]>;
310-
fn r_try_return_mutsliceu8(s: &mut [u8]) -> Result<&mut [u8]>;
310+
unsafe fn r_try_return_sliceu8<'a>(s: &'a [u8]) -> Result<&'a [u8]>;
311+
unsafe fn r_try_return_mutsliceu8<'a>(s: &'a mut [u8]) -> Result<&'a mut [u8]>;
311312

312313
fn get(self: &R) -> usize;
313314
fn set(self: &mut R, n: usize) -> usize;

tests/ffi/module.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ pub mod ffi2 {
5454
fn c_return_trivial_ns_ptr() -> UniquePtr<G>;
5555
fn c_return_trivial_ns() -> G;
5656
fn c_return_opaque_ptr() -> UniquePtr<E>;
57-
fn c_return_opaque_mut_pin(e: Pin<&mut E>) -> Pin<&mut E>;
57+
fn c_return_opaque_mut_pin<'a>(e: Pin<&'a mut E>) -> Pin<&'a mut E>;
5858
fn c_return_ns_opaque_ptr() -> UniquePtr<F>;
5959
fn c_return_ns_unique_ptr() -> UniquePtr<H>;
6060
fn c_take_ref_ns_c(h: &H);

tests/test.rs

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,15 @@ fn test_c_return() {
4343
assert_eq!(2020, ffi::c_return_box().0);
4444
ffi::c_return_unique_ptr();
4545
ffi2::c_return_ns_unique_ptr();
46-
assert_eq!(2020, *ffi::c_return_ref(&shared));
47-
assert_eq!(2020, *ffi::c_return_ns_ref(&ns_shared));
48-
assert_eq!(2020, *ffi::c_return_nested_ns_ref(&nested_ns_shared));
49-
assert_eq!("2020", ffi::c_return_str(&shared));
46+
assert_eq!(2020, *unsafe { ffi::c_return_ref(&shared) });
47+
assert_eq!(2020, *unsafe { ffi::c_return_ns_ref(&ns_shared) });
48+
assert_eq!(2020, *unsafe {
49+
ffi::c_return_nested_ns_ref(&nested_ns_shared)
50+
});
51+
assert_eq!("2020", unsafe { ffi::c_return_str(&shared) });
5052
assert_eq!(
5153
b"2020\0",
52-
cast::c_char_to_unsigned(ffi::c_return_slice_char(&shared)),
54+
cast::c_char_to_unsigned(unsafe { ffi::c_return_slice_char(&shared) }),
5355
);
5456
assert_eq!("2020", ffi::c_return_rust_string());
5557
assert_eq!("Hello \u{fffd}World", ffi::c_return_rust_string_lossy());
@@ -107,9 +109,15 @@ fn test_c_try_return() {
107109
ffi::c_fail_return_primitive().unwrap_err().what(),
108110
);
109111
assert_eq!(2020, ffi::c_try_return_box().unwrap().0);
110-
assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap());
111-
assert_eq!("2020", ffi::c_try_return_str("2020").unwrap());
112-
assert_eq!(b"2020", ffi::c_try_return_sliceu8(b"2020").unwrap());
112+
assert_eq!(
113+
"2020",
114+
*unsafe { ffi::c_try_return_ref(&"2020".to_owned()) }.unwrap()
115+
);
116+
assert_eq!("2020", unsafe { ffi::c_try_return_str("2020") }.unwrap());
117+
assert_eq!(
118+
b"2020",
119+
unsafe { ffi::c_try_return_sliceu8(b"2020") }.unwrap()
120+
);
113121
assert_eq!("2020", ffi::c_try_return_rust_string().unwrap());
114122
assert_eq!("2020", &*ffi::c_try_return_unique_ptr_string().unwrap());
115123
}
@@ -189,9 +197,11 @@ fn test_c_take() {
189197
check!(ffi::c_take_ref_rust_vec(&test_vec));
190198
check!(ffi::c_take_ref_rust_vec_index(&test_vec));
191199
check!(ffi::c_take_ref_rust_vec_copy(&test_vec));
192-
check!(ffi::c_take_ref_shared_string(&ffi::SharedString {
193-
msg: "2020".to_owned()
194-
}));
200+
check!(unsafe {
201+
ffi::c_take_ref_shared_string(&ffi::SharedString {
202+
msg: "2020".to_owned(),
203+
})
204+
});
195205
let ns_shared_test_vec = vec![ffi::AShared { z: 1010 }, ffi::AShared { z: 1011 }];
196206
check!(ffi::c_take_rust_vec_ns_shared(ns_shared_test_vec));
197207
let nested_ns_shared_test_vec = vec![ffi::ABShared { z: 1010 }, ffi::ABShared { z: 1011 }];
@@ -256,15 +266,19 @@ fn test_c_method_calls() {
256266
assert_eq!(2021, unique_ptr.pin_mut().set(2021));
257267
assert_eq!(2021, unique_ptr.get());
258268
assert_eq!(2021, unique_ptr.get2());
259-
assert_eq!(2021, *unique_ptr.getRef());
269+
assert_eq!(2021, *unsafe { unique_ptr.getRef() });
260270
assert_eq!(2021, unsafe { &mut *unique_ptr.as_mut_ptr() }.get());
261271
assert_eq!(2021, unsafe { &*unique_ptr.as_ptr() }.get());
262-
assert_eq!(2021, *unique_ptr.pin_mut().getMut());
272+
assert_eq!(2021, *unsafe { unique_ptr.pin_mut().getMut() });
263273
assert_eq!(2022, unique_ptr.pin_mut().set_succeed(2022).unwrap());
264274
assert!(unique_ptr.pin_mut().get_fail().is_err());
265275
assert_eq!(2021, ffi::Shared { z: 0 }.c_method_on_shared());
266-
assert_eq!(2022, *ffi::Shared { z: 2022 }.c_method_ref_on_shared());
267-
assert_eq!(2023, *ffi::Shared { z: 2023 }.c_method_mut_on_shared());
276+
assert_eq!(2022, *unsafe {
277+
ffi::Shared { z: 2022 }.c_method_ref_on_shared()
278+
});
279+
assert_eq!(2023, *unsafe {
280+
ffi::Shared { z: 2023 }.c_method_mut_on_shared()
281+
});
268282

269283
let val = 42;
270284
let mut array = ffi::Array {

tests/ui/bad_explicit_impl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ mod ffi {
44
x: u8,
55
}
66

7-
impl fn() -> &S {}
7+
impl<'a> fn() -> &'a S {}
88
}
99

1010
fn main() {}

tests/ui/bad_explicit_impl.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: unsupported Self type of explicit impl
22
--> tests/ui/bad_explicit_impl.rs:7:5
33
|
4-
7 | impl fn() -> &S {}
5-
| ^^^^^^^^^^^^^^^^^^
4+
7 | impl<'a> fn() -> &'a S {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^

tests/ui/mut_return.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ mod ffi {
77
unsafe extern "C++" {
88
type Thing;
99

10-
fn f(t: &Thing) -> Pin<&mut CxxString>;
11-
unsafe fn g(t: &Thing) -> Pin<&mut CxxString>;
12-
fn h(t: Box<Mut>) -> Pin<&mut CxxString>;
10+
fn f<'a>(t: &'a Thing) -> Pin<&'a mut CxxString>;
11+
unsafe fn g<'a>(t: &'a Thing) -> Pin<&'a mut CxxString>;
12+
fn h<'a>(t: Box<Mut>) -> Pin<&'a mut CxxString>;
1313
fn i<'a>(t: Box<Mut<'a>>) -> Pin<&'a mut CxxString>;
14-
fn j(t: &Thing) -> &mut [u8];
14+
fn j<'a>(t: &'a Thing) -> &'a mut [u8];
1515
}
1616
}
1717

tests/ui/mut_return.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
error: &mut return type is not allowed unless there is a &mut argument
22
--> tests/ui/mut_return.rs:10:9
33
|
4-
10 | fn f(t: &Thing) -> Pin<&mut CxxString>;
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
10 | fn f<'a>(t: &'a Thing) -> Pin<&'a mut CxxString>;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66

77
error: &mut return type is not allowed unless there is a &mut argument
88
--> tests/ui/mut_return.rs:14:9
99
|
10-
14 | fn j(t: &Thing) -> &mut [u8];
11-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
10+
14 | fn j<'a>(t: &'a Thing) -> &'a mut [u8];
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)