-
Notifications
You must be signed in to change notification settings - Fork 158
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
feature: add #[salsa::supertype]
#677
base: master
Are you sure you want to change the base?
Conversation
✅ Deploy Preview for salsa-rs canceled.
|
e79e1d9
to
67bd27d
Compare
CodSpeed Performance ReportMerging #677 will not alter performanceComparing Summary
|
@@ -66,6 +67,11 @@ pub fn interned(args: TokenStream, input: TokenStream) -> TokenStream { | |||
interned::interned(args, input) | |||
} | |||
|
|||
#[proc_macro_derive(supertype)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[proc_macro_derive(supertype)] | |
#[proc_macro_derive(Supertype)] |
/// For an entity struct `Foo` with fields `f1: T1, ..., fN: TN`, we generate... | ||
/// | ||
/// * the "id struct" `struct Foo(salsa::Id)` | ||
/// * the entity ingredient, which maps the id fields to the `Id` | ||
/// * for each value field, a function ingredient |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment seems wrong (given it was copied verbatim from another one attr/derive)
/// This method is used to create ingredient indices. Note, it does *not* create the ingredients | ||
/// themselves, that is the job of [`Zalsa::add_or_lookup_jar_by_type()`]. This method only creates | ||
/// or lookup the indices. Naturally, implementors may call `add_or_lookup_jar_by_type()` to | ||
/// create the ingredient, but they do not must, e.g. enums recursively call |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// create the ingredient, but they do not must, e.g. enums recursively call | |
/// create the ingredient, but they aren't required to, e.g. enums recursively call |
/// This method is used to support nested Salsa enums, a.k.a.: | ||
/// ```ignore | ||
/// #[salsa::input] | ||
/// struct Input {} | ||
/// | ||
/// #[salsa::interned] | ||
/// struct Interned1 {} | ||
/// | ||
/// #[salsa::interned] | ||
/// struct Interned2 {} | ||
/// | ||
/// #[derive(Debug, salsa::Enum)] | ||
/// enum InnerEnum { | ||
/// Input(Input), | ||
/// Interned1(Interned1), | ||
/// } | ||
/// | ||
/// #[derive(Debug, salsa::Enum)] | ||
/// enum OuterEnum { | ||
/// InnerEnum(InnerEnum), | ||
/// Interned2(Interned2), | ||
/// } | ||
/// ``` | ||
/// Imagine `OuterEnum` got a [`salsa::Id`][Id] and it wants to know which variant it belongs to. | ||
/// It cannot ask each variant "what is your ingredient index?" and compare, because `InnerEnum` | ||
/// has multiple possible ingredient indices. | ||
/// | ||
/// It could ask each variant "is this value yours?" and then invoke [`FromId`][crate::id::FromId] | ||
/// with the correct variant, but that will duplicate the work: now `InnerEnum` will have to do | ||
/// the same thing for its variants. | ||
/// | ||
/// Instead, we keep track of the [`TypeId`] of the ID struct, and ask each variant to "cast" it. If | ||
/// it succeeds, we return that value; if not, we go to the next variant. | ||
/// | ||
/// Why `TypeId` and not `IngredientIndex`? Because it's cheaper and easier. The `TypeId` is readily | ||
/// available at compile time, while the `IngredientIndex` requires a runtime lookup. | ||
fn cast(id: Id, type_id: TypeId) -> Option<Self>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the implication of this if you have nested enums where some of the variants are of the same salsa struct? I presume it will just always cast to the "first" of the duplicated variants being walked right? Might be good to note that down somewhere (in that we do not preserve variant identity when faced with duplicate variants)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I thought this won't be a problem, because the use-case seems dubious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I don't think there is any point in having a differing behavior here, just wanted to clarify. So your enums being sets analogy makes sense.
Of note is that this implements a different design of #578, this design here having the benefit of retaining the enum being an enum (where as the issue would've transformed it into just another id that you'd need to access variants via methods). |
Hmm that sounds problematic, I don't see how adding an option to the macro fixes this? Are you saying we'd want to opt out of those impls in rust-analyzer until we replaced the type interner with salsa (by using the new trait solver)? |
Yes. In the meantime, Jack has also asked us to avoid making changes to the interner, as his changes depend on it being in place until Salsa's interned structs can natively handle/represent a |
/// atomicity), so other ingredients could not be created. | ||
/// | ||
/// Only tracked fns use this. | ||
fn create_dependencies(_zalsa: &Zalsa) -> IngredientIndices |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like calling this dependencies as it may cause confusion with query (tracked function) dependencies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wait, what'd you call this instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well they are dependencies, but a different kind, unsure about a better name though
Understandable but won't this have effects on the incrementality of rust-analyzer? |
Propopsing a different store scheme davidbarsky#1 that wastes a tiny bit of memory for faster Edit: It also skips the mapping entirely now for structs, so you only pay perf / memory usage for enums you use now. |
I don't think so, or at the very least, I haven't noticed any issues while running rust-analyzer under new Salsa, as I have been for the past month. For what it's worth, I think these test failures go away if we run the test suite under |
Or "supertypes" as they are called in salsa-rs#578. They are of the form: ``` enum Enum { Input(Input), Interned(Interned), ... } ```
Drop unnecesary ingredient- and memo-index mapping for structs
ce8ca57
to
2864e76
Compare
This PR implements #578 and is (currently) stacked atop of #676. All authorship/credit for this work goes to @ChayimFriedman2 for the implementation, I largely renamed some stuff.
Two notes on this PR:
macro_rules!
macro for#[salsa::supertype]
. We can add it, need be.PartialEq
implementations because they caused a bunch of test failures in rust-analyzer. Those test failures stemmed from rust-analyzer's native, non-Salsa powered interner, which persists even after we changed databases in tests! For more details, see what Chayim wrote on Zulip. Instead of commenting things out, I'll make these options on the attribute macro.