Skip to content

Preserve attributes on interned/tracked struct fields #905

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions components/salsa-macro-rules/src/setup_input_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ macro_rules! setup_input_struct {
// Indices for each field from 0..N -- must be unsuffixed (e.g., `0`, `1`).
field_indices: [$($field_index:tt),*],

// Attributes for each field
field_attrs: [$([$(#[$field_attr:meta]),*]),*],

// Fields that are required (have no default value). Each item is the fields name and type.
required_fields: [$($required_field_id:ident $required_field_ty:ty),*],

Expand Down Expand Up @@ -176,6 +179,7 @@ macro_rules! setup_input_struct {
}

$(
$(#[$field_attr])*
$field_getter_vis fn $field_getter_id<'db, $Db>(self, db: &'db $Db) -> $zalsa::return_mode_ty!($field_option, 'db, $field_ty)
where
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
Expand Down
4 changes: 4 additions & 0 deletions components/salsa-macro-rules/src/setup_interned_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ macro_rules! setup_interned_struct {
// Indexed types for each field (T0, T1, ...)
field_indexed_tys: [$($indexed_ty:ident),*],

// Attrs for each field.
field_attrs: [$([$(#[$field_attr:meta]),*]),*],

// Number of fields
num_fields: $N:literal,

Expand Down Expand Up @@ -211,6 +214,7 @@ macro_rules! setup_interned_struct {
}

$(
$(#[$field_attr])*
$field_getter_vis fn $field_getter_id<$Db>(self, db: &'db $Db) -> $zalsa::return_mode_ty!($field_option, 'db, $field_ty)
where
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
Expand Down
6 changes: 6 additions & 0 deletions components/salsa-macro-rules/src/setup_tracked_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ macro_rules! setup_tracked_struct {
// (see e.g. @return_mode below).
untracked_options: [$($untracked_option:tt),*],

// Attrs for each field.
tracked_field_attrs: [$([$(#[$tracked_field_attr:meta]),*]),*],
untracked_field_attrs: [$([$(#[$untracked_field_attr:meta]),*]),*],

// Number of tracked fields.
num_tracked_fields: $N:literal,

Expand Down Expand Up @@ -256,6 +260,7 @@ macro_rules! setup_tracked_struct {
}

$(
$(#[$tracked_field_attr])*
$tracked_getter_vis fn $tracked_getter_id<$Db>(self, db: &$db_lt $Db) -> $crate::return_mode_ty!($tracked_option, $db_lt, $tracked_ty)
where
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
Expand All @@ -272,6 +277,7 @@ macro_rules! setup_tracked_struct {
)*

$(
$(#[$untracked_field_attr])*
$untracked_getter_vis fn $untracked_getter_id<$Db>(self, db: &$db_lt $Db) -> $crate::return_mode_ty!($untracked_option, $db_lt, $untracked_ty)
where
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
Expand Down
2 changes: 2 additions & 0 deletions components/salsa-macros/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ impl Macro {
let field_options = salsa_struct.field_options();
let field_tys = salsa_struct.field_tys();
let field_durability_ids = salsa_struct.field_durability_ids();
let field_attrs = salsa_struct.field_attrs();
let is_singleton = self.args.singleton.is_some();
let generate_debug_impl = salsa_struct.generate_debug_impl();

Expand All @@ -125,6 +126,7 @@ impl Macro {
field_setters: [#(#field_vis #field_setter_ids),*],
field_tys: [#(#field_tys),*],
field_indices: [#(#field_indices),*],
field_attrs: [#([#(#field_attrs),*]),*],
required_fields: [#(#required_fields),*],
field_durability_ids: [#(#field_durability_ids),*],
num_fields: #num_fields,
Expand Down
2 changes: 2 additions & 0 deletions components/salsa-macros/src/interned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ impl Macro {
let field_options = salsa_struct.field_options();
let field_tys = salsa_struct.field_tys();
let field_indexed_tys = salsa_struct.field_indexed_tys();
let field_unused_attrs = salsa_struct.field_attrs();
let generate_debug_impl = salsa_struct.generate_debug_impl();
let has_lifetime = salsa_struct.generate_lifetime();
let id = salsa_struct.id();
Expand Down Expand Up @@ -147,6 +148,7 @@ impl Macro {
field_tys: [#(#field_tys),*],
field_indices: [#(#field_indices),*],
field_indexed_tys: [#(#field_indexed_tys),*],
field_attrs: [#([#(#field_unused_attrs),*]),*],
num_fields: #num_fields,
generate_debug_impl: #generate_debug_impl,
unused_names: [
Expand Down
24 changes: 24 additions & 0 deletions components/salsa-macros/src/salsa_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ pub(crate) struct SalsaField<'s> {
pub(crate) has_no_eq_attr: bool,
get_name: syn::Ident,
set_name: syn::Ident,
unknown_attrs: Vec<&'s syn::Attribute>,
}

const BANNED_FIELD_NAMES: &[&str] = &["from", "new"];
Expand Down Expand Up @@ -316,6 +317,22 @@ where
.collect()
}

pub(crate) fn field_attrs(&self) -> Vec<&[&syn::Attribute]> {
self.fields.iter().map(|f| &*f.unknown_attrs).collect()
}

pub(crate) fn tracked_field_attrs(&self) -> Vec<&[&syn::Attribute]> {
self.tracked_fields_iter()
.map(|f| &*f.1.unknown_attrs)
.collect()
}

pub(crate) fn untracked_field_attrs(&self) -> Vec<&[&syn::Attribute]> {
self.untracked_fields_iter()
.map(|f| &*f.1.unknown_attrs)
.collect()
}

pub(crate) fn field_options(&self) -> Vec<TokenStream> {
self.fields.iter().map(SalsaField::options).collect()
}
Expand Down Expand Up @@ -377,15 +394,22 @@ impl<'s> SalsaField<'s> {
has_no_eq_attr: false,
get_name,
set_name,
unknown_attrs: Default::default(),
};

// Scan the attributes and look for the salsa attributes:
for attr in &field.attrs {
let mut handled = false;
for (fa, func) in FIELD_OPTION_ATTRIBUTES {
if attr.path().is_ident(fa) {
func(attr, &mut result);
handled = true;
break;
}
}
if !handled {
result.unknown_attrs.push(attr);
}
}

// Validate return mode
Expand Down
6 changes: 6 additions & 0 deletions components/salsa-macros/src/tracked_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ impl Macro {
let tracked_tys = salsa_struct.tracked_tys();
let untracked_tys = salsa_struct.untracked_tys();

let tracked_field_unused_attrs = salsa_struct.tracked_field_attrs();
let untracked_field_unused_attrs = salsa_struct.untracked_field_attrs();

let num_tracked_fields = salsa_struct.num_tracked_fields();
let generate_debug_impl = salsa_struct.generate_debug_impl();

Expand Down Expand Up @@ -151,6 +154,9 @@ impl Macro {
tracked_options: [#(#tracked_options),*],
untracked_options: [#(#untracked_options),*],

tracked_field_attrs: [#([#(#tracked_field_unused_attrs),*]),*],
untracked_field_attrs: [#([#(#untracked_field_unused_attrs),*]),*],

num_tracked_fields: #num_tracked_fields,
generate_debug_impl: #generate_debug_impl,
unused_names: [
Expand Down
4 changes: 2 additions & 2 deletions src/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ impl Page {
}

#[inline]
fn assert_type<T: Slot>(&self) -> PageView<T> {
fn assert_type<T: Slot>(&self) -> PageView<'_, T> {
assert_eq!(
self.slot_type_id,
TypeId::of::<T>(),
Expand All @@ -388,7 +388,7 @@ impl Page {
PageView(self, PhantomData)
}

fn cast_type<T: Slot>(&self) -> Option<PageView<T>> {
fn cast_type<T: Slot>(&self) -> Option<PageView<'_, T>> {
if self.slot_type_id == TypeId::of::<T>() {
Some(PageView(self, PhantomData))
} else {
Expand Down
9 changes: 9 additions & 0 deletions tests/compile-fail/input_struct_unknown_attributes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#[salsa::input]
struct InputWithUnknownAttrs {
/// Doc comment
field: u32,
#[anything]
field2: u32,
}

fn main() {}
5 changes: 5 additions & 0 deletions tests/compile-fail/input_struct_unknown_attributes.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: cannot find attribute `anything` in this scope
--> tests/compile-fail/input_struct_unknown_attributes.rs:5:7
|
5 | #[anything]
| ^^^^^^^^
11 changes: 11 additions & 0 deletions tests/compile-fail/interned_struct_unknown_attribute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#[salsa::interned]
struct UnknownAttributeInterned {
/// Test doc comment
field: bool,
#[unknown_attr]
field2: bool,
#[salsa::tracked]
wrong_tracked: bool,
}

fn main() {}
13 changes: 13 additions & 0 deletions tests/compile-fail/interned_struct_unknown_attribute.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error: only a single lifetime parameter is accepted
--> tests/compile-fail/interned_struct_unknown_attribute.rs:1:1
|
1 | #[salsa::interned]
| ^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `salsa::interned` (in Nightly builds, run with -Z macro-backtrace for more info)

error: cannot find attribute `unknown_attr` in this scope
--> tests/compile-fail/interned_struct_unknown_attribute.rs:5:7
|
5 | #[unknown_attr]
| ^^^^^^^^^^^^
14 changes: 14 additions & 0 deletions tests/compile-fail/tracked_struct_unknown_attribute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#[salsa::tracked]
struct UnknownAttributeTrackedStruct<'db> {
#[tracked]
tracked: bool,
#[unknown_attr]
field: bool,
#[salsa::tracked]
wrong_tracked: bool,
/// TestDocComment
/// TestDocComment
field_with_doc: bool
}

fn main() {}
13 changes: 13 additions & 0 deletions tests/compile-fail/tracked_struct_unknown_attribute.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error: only a single lifetime parameter is accepted
--> tests/compile-fail/tracked_struct_unknown_attribute.rs:1:1
|
1 | #[salsa::tracked]
| ^^^^^^^^^^^^^^^^^
|
= note: this error originates in the attribute macro `salsa::tracked` (in Nightly builds, run with -Z macro-backtrace for more info)

error: cannot find attribute `unknown_attr` in this scope
--> tests/compile-fail/tracked_struct_unknown_attribute.rs:5:7
|
5 | #[unknown_attr]
| ^^^^^^^^^^^^
4 changes: 2 additions & 2 deletions tests/cycle_tracked_own_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ enum Type<'db> {
}

impl Type<'_> {
fn class(&self) -> Option<Class> {
fn class(&self) -> Option<Class<'_>> {
match self {
Type::Class(class) => Some(*class),
Type::Unknown => None,
Expand Down Expand Up @@ -79,7 +79,7 @@ fn infer_type_param<'db>(db: &'db dyn salsa::Database, node: TypeParamNode) -> T
}
}

fn infer_class_initial(_db: &dyn Database, _node: ClassNode) -> Type {
fn infer_class_initial(_db: &'_ dyn Database, _node: ClassNode) -> Type<'_> {
Type::Unknown
}

Expand Down