Skip to content

Commit dcff78e

Browse files
committed
pwrite: fix lifetime clash in pwrite impl, generate a fresh lifetime for the extra reference derive; add a smaller debug test file for easier expansion of macros when debugging
1 parent 1e27733 commit dcff78e

3 files changed

Lines changed: 60 additions & 18 deletions

File tree

scroll_derive/src/lib.rs

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,35 @@
11
#![recursion_limit = "1024"]
22

33
extern crate proc_macro;
4+
use proc_macro2::Span;
45
use quote::{ToTokens, quote};
56

67
use proc_macro::TokenStream;
78

9+
fn extract_lifetime(
10+
gp: &syn::punctuated::Punctuated<syn::GenericParam, syn::token::Comma>,
11+
) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
12+
let mut lifetimes = gp
13+
.iter()
14+
.filter_map(|param: &syn::GenericParam| match param {
15+
syn::GenericParam::Lifetime(lifetime) => Some(lifetime.lifetime.clone()),
16+
_ => None,
17+
})
18+
.collect::<Vec<_>>();
19+
if lifetimes.len() > 1 {
20+
panic!("Pread cannot be derived for multiple lifetimes")
21+
}
22+
let lifetime = lifetimes
23+
.pop()
24+
.unwrap_or(syn::Lifetime::new("'a", Span::call_site()));
25+
// alpha rename/make the thing fresh
26+
let alpha = format!("'{}_fresh", lifetime.ident.to_string());
27+
(
28+
lifetime.to_token_stream(),
29+
syn::Lifetime::new(&alpha.to_string(), lifetime.span()).to_token_stream(),
30+
)
31+
}
32+
833
fn impl_field(
934
ident: &proc_macro2::TokenStream,
1035
ty: &syn::Type,
@@ -122,17 +147,7 @@ fn impl_struct(
122147
p => quote! { #p },
123148
});
124149

125-
let mut lifetimes = gp
126-
.iter()
127-
.filter_map(|param: &syn::GenericParam| match param {
128-
syn::GenericParam::Lifetime(lifetime) => Some(quote! { #lifetime }),
129-
_ => None,
130-
})
131-
.collect::<Vec<_>>();
132-
if lifetimes.len() > 1 {
133-
panic!("Pread cannot be derived for multiple lifetimes")
134-
}
135-
let lifetime = lifetimes.pop().unwrap_or(quote! { 'a });
150+
let (lifetime, _fresh_lifetime) = extract_lifetime(gp);
136151
let gn = quote! { #gl #( #gn ),* #gg };
137152
// drop the lifetime from our generic params, since we already grabbed it
138153
let initial_generic_params = gp
@@ -296,14 +311,16 @@ fn impl_try_into_ctx(
296311
p => quote! { #p },
297312
});
298313
let gn = quote! { #gl #( #gn ),* #gg };
314+
// it's always important to keep it _fresh_ when we pwrite
315+
let (_lifetime, fresh_lifetime) = extract_lifetime(gp);
299316
let gwref = if !gp.is_empty() {
300317
let gi: Vec<_> = gp.iter().filter_map(|param: &syn::GenericParam| match param {
301318
syn::GenericParam::Type(t) => {
302319
let ident = &t.ident;
303320
Some(quote! {
304-
&'a #ident : ::scroll::ctx::TryIntoCtx<::scroll::Endian>,
305-
::scroll::Error: ::std::convert::From<<&'a #ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error>,
306-
<&'a #ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error: ::std::convert::From<scroll::Error>
321+
&#fresh_lifetime #ident : ::scroll::ctx::TryIntoCtx<::scroll::Endian>,
322+
::scroll::Error: ::std::convert::From<<&#fresh_lifetime #ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error>,
323+
<&#fresh_lifetime #ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error: ::std::convert::From<scroll::Error>
307324
})
308325
},
309326
syn::GenericParam::Lifetime(_) => None,
@@ -336,7 +353,7 @@ fn impl_try_into_ctx(
336353
};
337354

338355
quote! {
339-
impl<'a, #gp > ::scroll::ctx::TryIntoCtx<::scroll::Endian> for &'a #name #gn #gwref {
356+
impl<#fresh_lifetime, #gp > ::scroll::ctx::TryIntoCtx<::scroll::Endian> for &#fresh_lifetime #name #gn #gwref {
340357
type Error = ::scroll::Error;
341358
#[inline]
342359
fn try_into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<usize, Self::Error> {

scroll_derive/tests/debug.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use scroll_derive::*;
2+
3+
// These are repetitive tests but they're also problematic ones when derive macro fails
4+
// it's easier to test/debug macro by commenting all but one that's failing and then running:
5+
// RUSTFLAGS=-Zmacro-backtrace cargo +nightly expand --test debug
6+
// and then investigating from there what to fix in the macro itself
7+
8+
#[derive(Pread, Pwrite)]
9+
#[repr(C)]
10+
struct Data8<T, Y> {
11+
ids: [T; 3],
12+
xyz: Y,
13+
}
14+
15+
//#[derive(Debug, PartialEq, Eq, Pread, Pwrite, IOread, IOwrite, SizeWith)]
16+
#[derive(Pread, Pwrite, SizeWith, IOread, IOwrite)]
17+
struct Data10I(u8, u16);
18+
19+
#[derive(Debug, Pread, Pwrite)]
20+
struct Life1<'b> {
21+
#[scroll(ctx = scroll::ctx::StrCtx::Length(6))]
22+
ids: &'b str,
23+
#[scroll(ctx = 5)]
24+
data: &'b [u8],
25+
}

scroll_derive/tests/tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ fn test_pread_lifetime() {
303303
assert_eq!(b2, bytes);
304304
}
305305

306-
#[derive(Debug, Pread)]
306+
#[derive(Debug, Pread, Pwrite)]
307307
pub struct FloofHeader {
308308
pub boop: u8,
309309
pub length: u16,
@@ -316,7 +316,7 @@ pub trait QuuxHeaderApi {
316316
fn msg_len(&self) -> usize;
317317
}
318318

319-
#[derive(Debug, Copy, Clone, Pread)]
319+
#[derive(Debug, Copy, Clone, Pread, Pwrite)]
320320
pub struct QuuxHeader {
321321
pub typ: u8,
322322
pub txn_id: u16,
@@ -330,7 +330,7 @@ impl QuuxHeaderApi for QuuxHeader {
330330
}
331331
}
332332

333-
#[derive(Debug, Pread)]
333+
#[derive(Debug, Pread, Pwrite)]
334334
pub struct Response<'a, T: QuuxHeaderApi> {
335335
pub floof: FloofHeader,
336336
pub quux: T,

0 commit comments

Comments
 (0)