Skip to content

internal: add tuple struct support in pin_data and pin_init! (V2)#155

Open
mqqz wants to merge 4 commits into
Rust-for-Linux:mainfrom
mqqz:add_tuple_structs_v2
Open

internal: add tuple struct support in pin_data and pin_init! (V2)#155
mqqz wants to merge 4 commits into
Rust-for-Linux:mainfrom
mqqz:add_tuple_structs_v2

Conversation

@mqqz

@mqqz mqqz commented May 22, 2026

Copy link
Copy Markdown
Contributor

Replaces #113.

This adds tuple struct support to #[pin_data], init!, and pin_init!.

The projected form of a tuple struct is also a tuple struct, so projected fields are accessed with native tuple syntax (.0, .1, ...), which keeps the generated API closer to
ordinary Rust and avoids exposing synthetic field names (also makes it easier fields are cfg'd out).

  • tuple struct projections from #[pin_data]
  • tuple struct initialization with indexed brace syntax:
    • pin_init!(Foo { 0 <- ..., 1: ... })
  • tuple struct initialization with constructor syntax:
    • pin_init!(Foo(...)) (but no <- e.g. Foo(a, <- b, c) is rejected)
  • support for tuple structs with generics, const generics, pinned drop, and fallible partial initialization rollback
  • support for tuple structs when fields or constructor arguments are removed by #[cfg] (Error out unless #[cfg] is on the final tuple field)

Notes

  • tuple projections use native tuple field access rather than synthetic names e.g. ._0 like last time.
  • cfg handling does not try to evaluate user #[cfg] conditions in the proc macro
  • instead, the macro generates the necessary cfg-dependent layouts and lets rustc select the active branch
  • #[cfg] is supported only on the final tuple field / constructor argument, when it does not change numbering.

Tests

Added coverage for:

  • basic tuple struct projection
  • brace and constructor tuple initialization syntax
  • duplicate / missing / invalid tuple field UI failures
  • generic and const-generic tuple structs
  • #[pin] interaction with !Unpin field types
  • pinned drop
  • fallible partial-init rollback
  • cfg-stripped tuple fields (last or otherwise)
  • cfg-stripped tuple constructor arguments
  • feature-dependent cfg field layouts

Comparison with the old PR

This replaces the earlier attempt in #113.

Compared with that PR, this version is intentionally narrower and easier to review:

  • less code motion/churn overall (my code always got motion 😎)
  • fewer refactors mixed into the feature work
  • tuple projections are tuple structs instead of relying on synthetic internal/public field names
  • cfg handling was redesigned to correctly handle cfg-stripped tuple fields and constructor arguments simplified to accept only on last field/arg. or error out
  • the test set was trimmed to the non-redundant cases, while keeping the important regressions and restoring the fallible partial-init rollback coverage

Closes: #85

Comment thread internal/src/init.rs Outdated
mqqz added 4 commits May 25, 2026 19:26
Add plumbing for tuple struct support to `#[pin_data]` by generating
tuple struct projections and tuple-aware pin-data metadata.

Keep the projection API native to Rust tuple structs, so projected
fields are accessed as `.0`, `.1`, etc. instead of introducing
synthetic public field names.

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
Expand `init!` and `pin_init!` to initialize tuple structs using both
indexed brace syntax and tuple-constructor syntax.

Add tuple-specific runtime and UI coverage for the basic initializer
forms, duplicate/missing fields, invalid indices, and syntax errors.

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
Extend tuple-struct coverage with the semantic cases that are easiest
to review independently from the implementation changes.

Add runtime coverage for generic and const-generic tuple structs,
`Unpin` behaviour with `!Unpin` field types, pinned drop, and fallible
partial-init rollback.

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
Support tuple structs whose final field or final constructor argument is removed by #[cfg]. This preserves tuple indices without needing to evaluate user cfgs in the proc macro.

Reject #[cfg] on earlier tuple fields and tuple constructor arguments, because those cases would require reindexing the remaining fields after cfg stripping. That is possible to generate, but the extra complexity is not justified for the tuple struct support added here.

Add regression tests for the supported final-field case and UI diagnostics for the unsupported index-shifting cases.

Signed-off-by: Mohamad Alsadhan <mo@sdhn.cc>
@mqqz mqqz force-pushed the add_tuple_structs_v2 branch from 1f2c279 to 2d2042e Compare May 25, 2026 16:32
Comment thread internal/src/init.rs
return Err(syn::Error::new_spanned(
attr,
"`#[cfg]` on tuple constructor arguments is only supported on the last argument",
));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be checked after parsing, thus would allow it to use the dcx mechanism and provide better error recovery.

Comment thread internal/src/pin_data.rs
Comment on lines +45 to +58
fn member_ident(member: &Member) -> Ident {
match member {
Member::Named(ident) => ident.clone(),
Member::Unnamed(Index { index, .. }) => format_ident!("_{index}"),
}
}

fn member_display_name(member: &Member) -> String {
match member {
Member::Named(ident) => format!("`{ident}`"),
Member::Unnamed(Index { index, .. }) => format!("index `{index}`"),
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be methods of FieldInfo

Comment thread internal/src/pin_data.rs
));
field_projections.push(quote!(#binding,));
}
field_bindings.push(quote!(ref mut #binding,));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to use this instead of .0, .1, etc?

Comment thread internal/src/pin_data.rs
#ty,
>,
});
field_values.push(quote! {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is changing how tuple struct is handled to be completely different from normal structs?

There's a reason that methods are used, because they support HRTB, so it makes it possible to add self-reference support later (#156)

Comment thread internal/src/init.rs
this: Option<This>,
path: Path,
brace_token: token::Brace,
close_span: Span,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
close_span: Span,
delim_close_span: Span,

Comment thread internal/src/init.rs
Comment on lines +577 to +580
if input.peek(Token![<-]) {
return Err(input.error(
"`<-` is not supported in tuple constructor syntax; use braces with indices, e.g. `Type { 0 <- init, 1: value }`",
));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd move this into parse_paren_initializer and drop the type.

Comment thread internal/src/pin_data.rs
field_bindings.push(quote!(ref mut #binding,));
}
let projection_init = if let Some(last_field) = fields.last() {
if let Some(cfg) = cfg_condition(&last_field.field.attrs) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should just unconditionally apply cfgs?

Comment thread internal/src/init.rs
let tuple_fields: Vec<_> = tuple_fields.into_iter().collect();
let mut fields = Punctuated::new();

for tuple_field in tuple_fields

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cfg rejection should happen before tuple struct is accepted at all, so there is no intermediate commit that allows it but performs incorrectly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Support for initializing tuple-structs

2 participants