Skip to content

Commit bc2e3f0

Browse files
committed
internal: init: implement automated field usage scanning
Signed-off-by: jkhall81 <jason.kei.hall@gmail.com>
1 parent 9a092f8 commit bc2e3f0

6 files changed

Lines changed: 92 additions & 38 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

1616
### Changed
1717

18+
- Automatically scan the initializer body in `[pin_]init!` to conditionally generate field
19+
accessors; this reduces stack bloat and prevents alignment errors on packed structs by
20+
only creating references when a field is actually used.
1821
- `#[pin_data]` now generates a `*Projection` struct similar to the `pin-project` crate.
1922
- Add initializer code blocks to `[try_][pin_]init!` macros: make initializer
2023
macros accept any number of `_: {/* arbitrary code */},` & make them run the
2124
code at that point.
2225
- Make the `[try_][pin_]init!` macros expose initialized fields via a `let`
2326
binding as `&mut T` or `Pin<&mut T>` for later fields.
2427
- Rewrote all proc-macros (`[pin_]init!`, `#[pin_data]`, `#[pinned_drop]`,
25-
`derive([Maybe]Zeroable)`), using `syn` with better diagnostics.
28+
`derive([Maybe]Zeroable)`), using `syn` with better diagnostics.
2629
- `derive([Maybe]Zeroable)` now support tuple structs.
2730
- `[pin_]init!` now supports attributes on fields (such as `#[cfg(...)]`).
2831
- Add a `#[default_error(<type>)]` attribute to `[pin_]init!` to override the

internal/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ proc-macro = true
1515
[dependencies]
1616
quote = "1.0"
1717
proc-macro2 = "1.0"
18-
syn = { version = "2.0.86", features = ["full", "parsing", "visit-mut"] }
18+
syn = { version = "2.0.86", features = ["full", "parsing", "visit", "visit-mut"] }
1919

2020
[build-dependencies]
2121
rustc_version = "0.4"

internal/src/init.rs

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,34 @@
22

33
use proc_macro2::{Span, TokenStream};
44
use quote::{format_ident, quote, quote_spanned};
5+
use std::collections::HashSet;
56
use syn::{
67
braced,
78
parse::{End, Parse},
89
parse_quote,
910
punctuated::Punctuated,
1011
spanned::Spanned,
11-
token, Attribute, Block, Expr, ExprCall, ExprPath, Ident, Path, Token, Type,
12+
token,
13+
visit::{self, Visit},
14+
Attribute, Block, Expr, ExprCall, ExprPath, Ident, Path, Token, Type,
1215
};
1316

1417
use crate::diagnostics::{DiagCtxt, ErrorGuaranteed};
1518

19+
struct AccessorScanner<'a> {
20+
field_names: HashSet<&'a Ident>,
21+
used_fields: HashSet<Ident>,
22+
}
23+
24+
impl<'a> Visit<'_> for AccessorScanner<'a> {
25+
fn visit_ident(&mut self, i: &Ident) {
26+
if self.field_names.contains(i) {
27+
self.used_fields.insert(i.clone());
28+
}
29+
visit::visit_ident(self, i);
30+
}
31+
}
32+
1633
pub(crate) struct Initializer {
1734
attrs: Vec<InitializerAttribute>,
1835
this: Option<This>,
@@ -145,6 +162,27 @@ pub(crate) fn expand(
145162
};
146163
// `mixed_site` ensures that the data is not accessible to the user-controlled code.
147164
let data = Ident::new("__data", Span::mixed_site());
165+
let mut field_names = HashSet::new();
166+
for field in &fields {
167+
if let Some(ident) = field.kind.ident() {
168+
field_names.insert(ident);
169+
}
170+
}
171+
let mut scanner = AccessorScanner {
172+
field_names,
173+
used_fields: HashSet::new(),
174+
};
175+
for field in &fields {
176+
match &field.kind {
177+
InitializerKind::Value {
178+
value: Some((_, expr)),
179+
..
180+
} => scanner.visit_expr(expr),
181+
InitializerKind::Init { value, .. } => scanner.visit_expr(value),
182+
InitializerKind::Code { block, .. } => scanner.visit_block(block),
183+
_ => {}
184+
}
185+
}
148186
let init_fields = init_fields(
149187
&fields,
150188
pinned,
@@ -153,6 +191,7 @@ pub(crate) fn expand(
153191
.any(|attr| matches!(attr, InitializerAttribute::DisableInitializedFieldAccess)),
154192
&data,
155193
&slot,
194+
&scanner.used_fields,
156195
);
157196
let field_check = make_field_check(&fields, init_kind, &path);
158197
Ok(quote! {{
@@ -239,6 +278,7 @@ fn init_fields(
239278
generate_initialized_accessors: bool,
240279
data: &Ident,
241280
slot: &Ident,
281+
used_fields: &HashSet<Ident>,
242282
) -> TokenStream {
243283
let mut guards = vec![];
244284
let mut guard_attrs = vec![];
@@ -272,13 +312,14 @@ fn init_fields(
272312
unsafe { &mut (*#slot).#ident }
273313
}
274314
};
275-
let accessor = generate_initialized_accessors.then(|| {
276-
quote! {
277-
#(#cfgs)*
278-
#[allow(unused_variables)]
279-
let #ident = #accessor;
280-
}
281-
});
315+
let accessor = (generate_initialized_accessors && used_fields.contains(ident))
316+
.then(|| {
317+
quote! {
318+
#(#cfgs)*
319+
#[allow(unused_variables)]
320+
let #ident = #accessor;
321+
}
322+
});
282323
quote! {
283324
#(#attrs)*
284325
{
@@ -326,13 +367,14 @@ fn init_fields(
326367
},
327368
)
328369
};
329-
let accessor = generate_initialized_accessors.then(|| {
330-
quote! {
331-
#(#cfgs)*
332-
#[allow(unused_variables)]
333-
let #ident = #accessor;
334-
}
335-
});
370+
let accessor = (generate_initialized_accessors && used_fields.contains(ident))
371+
.then(|| {
372+
quote! {
373+
#(#cfgs)*
374+
#[allow(unused_variables)]
375+
let #ident = #accessor;
376+
}
377+
});
336378
quote! {
337379
#(#attrs)*
338380
{

tests/packed.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#![allow(unknown_lints)]
2+
#![allow(unexpected_cfgs)]
3+
4+
use core::convert::Infallible;
5+
use pin_init::{pin_data, pin_init};
6+
7+
#[pin_data]
8+
struct MagicStruct {
9+
a: u32,
10+
b: u32,
11+
}
12+
13+
#[test]
14+
fn test_scanner_skips_unused() {
15+
let _ = pin_init!(MagicStruct {
16+
a: 1,
17+
b: 2,
18+
} ? Infallible);
19+
}
20+
21+
#[test]
22+
fn test_scanner_finds_used() {
23+
let _ = pin_init!(MagicStruct {
24+
a: 1,
25+
b: 2,
26+
_: {
27+
let _ = b;
28+
}
29+
} ? Infallible);
30+
}

tests/ui/compile-fail/init/packed_struct.rs

Lines changed: 0 additions & 11 deletions
This file was deleted.

tests/ui/compile-fail/init/packed_struct.stderr

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)