Skip to content

Commit

Permalink
Add support for [#default] and setting field-level durability
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser committed Aug 1, 2024
1 parent 0cae5c5 commit 805760d
Show file tree
Hide file tree
Showing 12 changed files with 239 additions and 16 deletions.
1 change: 1 addition & 0 deletions components/salsa-macro-rules/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
mod macro_if;
mod maybe_backdate;
mod maybe_clone;
mod maybe_default;
mod setup_accumulator_impl;
mod setup_input_struct;
mod setup_interned_struct;
Expand Down
4 changes: 2 additions & 2 deletions components/salsa-macro-rules/src/maybe_backdate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#[macro_export]
macro_rules! maybe_backdate {
(
($maybe_clone:ident, no_backdate),
($maybe_clone:ident, no_backdate, $maybe_default:ident),
$field_ty:ty,
$old_field_place:expr,
$new_field_place:expr,
Expand All @@ -20,7 +20,7 @@ macro_rules! maybe_backdate {
};

(
($maybe_clone:ident, backdate),
($maybe_clone:ident, backdate, $maybe_default:ident),
$field_ty:ty,
$old_field_place:expr,
$new_field_place:expr,
Expand Down
8 changes: 4 additions & 4 deletions components/salsa-macro-rules/src/maybe_clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
#[macro_export]
macro_rules! maybe_clone {
(
(no_clone, $maybe_backdate:ident),
(no_clone, $maybe_backdate:ident, $maybe_default:ident),
$field_ty:ty,
$field_ref_expr:expr,
) => {
$field_ref_expr
};

(
(clone, $maybe_backdate:ident),
(clone, $maybe_backdate:ident, $maybe_default:ident),
$field_ty:ty,
$field_ref_expr:expr,
) => {
Expand All @@ -23,15 +23,15 @@ macro_rules! maybe_clone {
#[macro_export]
macro_rules! maybe_cloned_ty {
(
(no_clone, $maybe_backdate:ident),
(no_clone, $maybe_backdate:ident, $maybe_default:ident),
$db_lt:lifetime,
$field_ty:ty
) => {
& $db_lt $field_ty
};

(
(clone, $maybe_backdate:ident),
(clone, $maybe_backdate:ident, $maybe_default:ident),
$db_lt:lifetime,
$field_ty:ty
) => {
Expand Down
32 changes: 32 additions & 0 deletions components/salsa-macro-rules/src/maybe_default.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/// Generate either `field_ref_expr` or `field_ty::default`
///
/// Used when generating an input's builder.
#[macro_export]
macro_rules! maybe_default {
(
($maybe_clone:ident, $maybe_backdate:ident, default),
$field_ty:ty,
$field_ref_expr:expr,
) => {
<$field_ty>::default()
};

(
($maybe_clone:ident, $maybe_backdate:ident, required),
$field_ty:ty,
$field_ref_expr:expr,
) => {
$field_ref_expr
};
}

#[macro_export]
macro_rules! maybe_default_tt {
(($maybe_clone:ident, $maybe_backdate:ident, default) => $($t:tt)*) => {
$($t)*
};

(($maybe_clone:ident, $maybe_backdate:ident, required) => $($t:tt)*) => {

};
}
56 changes: 47 additions & 9 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,12 @@ macro_rules! setup_input_struct {
// Indices for each field from 0..N -- must be unsuffixed (e.g., `0`, `1`).
field_indices: [$($field_index:tt),*],

// 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),*],

// Names for the field durability methods on the builder (typically `foo_durability`)
field_durability_ids: [$($field_durability_id:ident),*],

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

Expand Down Expand Up @@ -123,19 +129,20 @@ macro_rules! setup_input_struct {

impl $Struct {
#[inline]
pub fn $new_fn<$Db>(db: &$Db, $($field_id: $field_ty),*) -> Self
pub fn $new_fn<$Db>(db: &$Db, $($required_field_id: $required_field_ty),*) -> Self
where
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
$Db: ?Sized + salsa::Database,
{
Self::builder($($field_id,)*).new(db)
Self::builder($($required_field_id,)*).new(db)
}

pub fn builder($($field_id: $field_ty),*) -> <Self as $zalsa_struct::HasBuilder>::Builder
pub fn builder($($required_field_id: $required_field_ty),*) -> <Self as $zalsa_struct::HasBuilder>::Builder
{
// Implement `new` here instead of inside the builder module
// because $Configuration can't be named in `builder`.
impl builder::$Builder {
/// Creates the new input with the set values.
pub fn new<$Db>(self, db: &$Db) -> $Struct
where
// FIXME(rust-lang/rust#65991): The `db` argument *should* have the type `dyn Database`
Expand All @@ -148,7 +155,7 @@ macro_rules! setup_input_struct {
}
}

builder::new_builder($($field_id,)*)
builder::new_builder($($zalsa::maybe_default!($field_option, $field_ty, $field_id,)),*)
}

$(
Expand Down Expand Up @@ -238,25 +245,56 @@ macro_rules! setup_input_struct {
// These are standalone functions instead of methods on `Builder` to prevent
// that the enclosing module can call them.
pub(super) fn new_builder($($field_id: $field_ty),*) -> $Builder {
$Builder { fields: ($($field_id,)*), durability: $zalsa::Durability::default() }
$Builder {
fields: ($($field_id,)*),
durabilities: [$zalsa::Durability::default(); $N],
}
}

pub(super) fn builder_into_inner(builder: $Builder, revision: $zalsa::Revision) -> (($($field_ty,)*), $zalsa::Array<$zalsa::Stamp, $N>) {
let stamps = $zalsa::Array::new([$zalsa::stamp(revision, builder.durability); $N]);
let stamps = $zalsa::Array::new([
$($zalsa::stamp(revision, builder.durabilities[$field_index])),*
]);

(builder.fields, stamps)
}

pub struct $Builder {
/// The field values.
fields: ($($field_ty,)*),
durability: $zalsa::Durability,

/// The durabilities per field.
durabilities: [$zalsa::Durability; $N],
}

impl $Builder {
/// Sets the durability of all fields
/// Sets the durability of all fields.
///
/// Overrides any previously set durabilities.
pub fn durability(mut self, durability: $zalsa::Durability) -> Self {
self.durability = durability;
self.durabilities = [durability; $N];
self
}

$($zalsa::maybe_default_tt! { $field_option =>
/// Sets the value of the field `$field_id`.
#[must_use]
pub fn $field_id(mut self, value: $field_ty) -> Self
{
self.fields.$field_index = value;
self
}
})*

$(
/// Sets the durability for the field `$field_id`.
#[must_use]
pub fn $field_durability_id(mut self, durability: $zalsa::Durability) -> Self
{
self.durabilities[$field_index] = durability;
self
}
)*
}
}
};
Expand Down
6 changes: 6 additions & 0 deletions components/salsa-macros/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ impl SalsaStructAllowedOptions for InputStruct {
const ALLOW_ID: bool = false;

const HAS_LIFETIME: bool = false;

const ALLOW_DEFAULT: bool = true;
}

struct Macro {
Expand All @@ -85,8 +87,10 @@ impl Macro {
let field_vis = salsa_struct.field_vis();
let field_getter_ids = salsa_struct.field_getter_ids();
let field_setter_ids = salsa_struct.field_setter_ids();
let required_fields = salsa_struct.required_fields();
let field_options = salsa_struct.field_options();
let field_tys = salsa_struct.field_tys();
let field_durability_ids = salsa_struct.field_durability_ids();
let is_singleton = self.args.singleton.is_some();
let generate_debug_impl = salsa_struct.generate_debug_impl();

Expand All @@ -111,6 +115,8 @@ impl Macro {
field_setters: [#(#field_vis #field_setter_ids),*],
field_tys: [#(#field_tys),*],
field_indices: [#(#field_indices),*],
required_fields: [#(#required_fields),*],
field_durability_ids: [#(#field_durability_ids),*],
num_fields: #num_fields,
is_singleton: #is_singleton,
generate_debug_impl: #generate_debug_impl,
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 @@ -63,6 +63,8 @@ impl SalsaStructAllowedOptions for InternedStruct {
const ALLOW_ID: bool = false;

const HAS_LIFETIME: bool = true;

const ALLOW_DEFAULT: bool = false;
}

struct Macro {
Expand Down
62 changes: 61 additions & 1 deletion components/salsa-macros/src/salsa_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,16 @@ pub(crate) trait SalsaStructAllowedOptions: AllowedOptions {

/// Does this kind of struct have a `'db` lifetime?
const HAS_LIFETIME: bool;

/// Are `#[default]` fields allowed?
const ALLOW_DEFAULT: bool;
}

pub(crate) struct SalsaField<'s> {
field: &'s syn::Field,

pub(crate) has_id_attr: bool,
pub(crate) has_default_attr: bool,
pub(crate) has_ref_attr: bool,
pub(crate) has_no_eq_attr: bool,
get_name: syn::Ident,
Expand All @@ -64,6 +68,7 @@ const BANNED_FIELD_NAMES: &[&str] = &["from", "new"];
#[allow(clippy::type_complexity)]
pub(crate) const FIELD_OPTION_ATTRIBUTES: &[(&str, fn(&syn::Attribute, &mut SalsaField))] = &[
("id", |_, ef| ef.has_id_attr = true),
("default", |_, ef| ef.has_default_attr = true),
("return_ref", |_, ef| ef.has_ref_attr = true),
("no_eq", |_, ef| ef.has_no_eq_attr = true),
("get", |attr, ef| {
Expand Down Expand Up @@ -99,6 +104,7 @@ where
};

this.maybe_disallow_id_fields()?;
this.maybe_disallow_default_fields()?;

this.check_generics()?;

Expand Down Expand Up @@ -138,6 +144,31 @@ where
Ok(())
}

/// Disallow `#[default]` attributes on the fields of this struct.
///
/// If an `#[default]` field is found, return an error.
///
/// # Parameters
///
/// * `kind`, the attribute name (e.g., `input` or `interned`)
fn maybe_disallow_default_fields(&self) -> syn::Result<()> {
if A::ALLOW_DEFAULT {
return Ok(());
}

// Check if any field has the `#[id]` attribute.
for ef in &self.fields {
if ef.has_default_attr {
return Err(syn::Error::new_spanned(
ef.field,
format!("`#[id]` cannot be used with `#[salsa::{}]`", A::KIND),
));
}
}

Ok(())
}

/// Check that the generic parameters look as expected for this kind of struct.
fn check_generics(&self) -> syn::Result<()> {
if A::HAS_LIFETIME {
Expand Down Expand Up @@ -173,6 +204,21 @@ where
.collect()
}

pub(crate) fn required_fields(&self) -> Vec<TokenStream> {
self.fields
.iter()
.filter_map(|f| {
if f.has_default_attr {
None
} else {
let ident = f.field.ident.as_ref().unwrap();
let ty = &f.field.ty;
Some(quote!(#ident #ty))
}
})
.collect()
}

pub(crate) fn field_vis(&self) -> Vec<&syn::Visibility> {
self.fields.iter().map(|f| &f.field.vis).collect()
}
Expand All @@ -185,6 +231,13 @@ where
self.fields.iter().map(|f| &f.set_name).collect()
}

pub(crate) fn field_durability_ids(&self) -> Vec<syn::Ident> {
self.fields
.iter()
.map(|f| quote::format_ident!("{}_durability", f.field.ident.as_ref().unwrap()))
.collect()
}

pub(crate) fn field_tys(&self) -> Vec<&syn::Type> {
self.fields.iter().map(|f| &f.field.ty).collect()
}
Expand All @@ -205,7 +258,13 @@ where
syn::Ident::new("backdate", Span::call_site())
};

quote!((#clone_ident, #backdate_ident))
let default_ident = if f.has_default_attr {
syn::Ident::new("default", Span::call_site())
} else {
syn::Ident::new("required", Span::call_site())
};

quote!((#clone_ident, #backdate_ident, #default_ident))
})
.collect()
}
Expand Down Expand Up @@ -235,6 +294,7 @@ impl<'s> SalsaField<'s> {
field,
has_id_attr: false,
has_ref_attr: false,
has_default_attr: false,
has_no_eq_attr: false,
get_name,
set_name,
Expand Down
2 changes: 2 additions & 0 deletions components/salsa-macros/src/tracked_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ impl SalsaStructAllowedOptions for TrackedStruct {
const ALLOW_ID: bool = true;

const HAS_LIFETIME: bool = true;

const ALLOW_DEFAULT: bool = false;
}

struct Macro {
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ pub mod plumbing {
pub use salsa_macro_rules::maybe_backdate;
pub use salsa_macro_rules::maybe_clone;
pub use salsa_macro_rules::maybe_cloned_ty;
pub use salsa_macro_rules::maybe_default;
pub use salsa_macro_rules::maybe_default_tt;
pub use salsa_macro_rules::setup_accumulator_impl;
pub use salsa_macro_rules::setup_input_struct;
pub use salsa_macro_rules::setup_interned_struct;
Expand Down
Loading

0 comments on commit 805760d

Please sign in to comment.