Skip to content
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

Lower const parameters #7434

Open
Veykril opened this issue Jan 25, 2021 · 8 comments
Open

Lower const parameters #7434

Veykril opened this issue Jan 25, 2021 · 8 comments
Assignees
Labels
C-Architecture Big architectural things which we need to figure up-front (or suggestions for rewrites :0) ) C-feature Category: feature request E-hard S-actionable Someone could pick this issue up and work on it right now

Comments

@Veykril
Copy link
Member

Veykril commented Jan 25, 2021

Currently const params are not lowered at all:
https://github.com/rust-analyzer/rust-analyzer/blob/aa91a0268bae6deda964a9fdfcbdbd2d8ca5802f/crates/hir_def/src/path/lower.rs#L177-L178

These kind of define bodies, so they come with all the goodies(and problems like local items) that other items like functions, statics and consts come with, and just like those they should ideally be lowered lazily.

See corresponding zulip discussion https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/ConstArg.20lowering

@Veykril Veykril added E-hard C-Architecture Big architectural things which we need to figure up-front (or suggestions for rewrites :0) ) S-unactionable Issue requires feedback, design decisions or is blocked on other work labels Jan 25, 2021
@dickytamara

This comment has been minimized.

bors bot added a commit that referenced this issue Mar 4, 2022
11140: Preserve order of generic args r=HKalbasi a=HKalbasi

rust-lang/rust#90207 removed order restriction of generic args, i.e. const generics can now become before of type generics. We need to preserve this order to analyze correctly, and this PR does that.

It also simplifies implementation of const generics a bit IMO.

Implementing default generics the same problem of #7434, we need lower them to body and then evaluate them.


Co-authored-by: hkalbasi <hamidrezakalbasi@protonmail.com>
bors bot added a commit that referenced this issue Mar 14, 2022
11688: Add const generics r=HKalbasi a=HKalbasi

#7434 is not in this PR, so there is no evaluation of constants and no default const argument. But the rest (type inference and method resolution with chalk) should work.

Chalk is pedantic about kind of generic arguments, and will panic in case of mixing type and const arguments. I tried to add test that, but I'm not sure if it covers all cases.


Co-authored-by: hkalbasi <hamidrezakalbasi@protonmail.com>
@HKalbasi
Copy link
Member

If each constant needs a body for its own, locating them/allocating id for them will be hard. My suggestion is generalizing body to an expression store, and include expressions of constants in generics in the related definition's body. So for example:

fn foo<const N: usize = {2 + 2}>(x: Const<{2 + 2}>, y: Const<N>) -> Const<{2 + 2}> {
  let x: Const<{2 + 2}> = Const::<{2 + 2}>;
  x
}

all of 2 + 2 would get ExprId from function's body. Similarly, expressions inside const and static type annotations will be included inside of their bodies. And in this way, type alias will become a DefWithBody so it can lower and evaluate expressions. Body of a type alias has no meaningful root, so we may want to split Body into two abstractions, one with body_expr and params and one with the rest of fields (the expression allocator part).

Comments?

@flodiebold
Copy link
Member

flodiebold commented Mar 17, 2022

I think having a common expression store for all constants probably makes sense, but maybe not making that the body? How about we introduce something like an ExpressionStore and have Body contain one of those (basically what you're suggesting in the last sentence). So TypeAlias wouldn't be a DefWithBody.

@flodiebold flodiebold added S-actionable Someone could pick this issue up and work on it right now and removed S-unactionable Issue requires feedback, design decisions or is blocked on other work labels Apr 6, 2022
bors bot added a commit that referenced this issue Apr 7, 2022
11920: Consider types of const generics r=flodiebold a=HKalbasi

fix #11913 

We should emit type_mismatch in const generics, probably after #7434. Currently they will lead to a misleading, time of use type error (like the added test).


Co-authored-by: hkalbasi <hamidrezakalbasi@protonmail.com>
@jplatte
Copy link
Contributor

jplatte commented Sep 14, 2022

Currently const params are not lowered at all:
rust-analyzer/rust-analyzer@aa91a02/crates/hir_def/src/path/lower.rs#L177-L178

Seems to no longer be the case:

ast::GenericArg::ConstArg(arg) => {
let arg = ConstScalarOrPath::from_expr_opt(arg.expr());
args.push(GenericArg::Const(arg))
}

@Veykril Veykril closed this as completed Sep 14, 2022
@Veykril Veykril reopened this Sep 14, 2022
@Veykril
Copy link
Member Author

Veykril commented Sep 14, 2022

Actually we still don't lower the default expressions of them I think?

@HKalbasi
Copy link
Member

Current lowering is just a bandaid. It only supports scalars (like 2) and simple names (like N).

bors added a commit that referenced this issue May 29, 2023
MIR episode 6

This PR separates monomorphization from interpreting, and add a monomorphization query to cache the results. Together with making layout queries returning `Arc<Layout>` instead of `Layout` (did you know that `Layout` is a 312 byte struct with a couple of vectors, so it is very costly to clone? I thought it should be very small and cheap) it makes mir interpreting an order of magnitude faster in warmed calls.

It still can't evaluate no test in the r-a repo, but all tests that I tried are hitting #7434 so I hope after that it will become able to interpret some real world test.
bors added a commit that referenced this issue Jun 12, 2023
Lower const params with a bad id

cc #7434

This PR adds an `InTypeConstId` which is a `DefWithBodyId` and lower const generic parameters into bodies using it, and evaluate them with the mir interpreter. I think this is the last unimplemented const generic feature relative to rustc stable.

But there is a problem: The id used in the `InTypeConstId` is the raw `FileAstId`, which changes frequently. So these ids and their bodies will be invalidated very frequently, which is bad for incremental analysis.

Due this problem, I disabled lowering for local crates (in library crate the id is stable since files won't be changed). This might be overreacting (const generic expressions are usually small, maybe it would be better enabled with bad performance than disabled) but it makes motivation for doing it in the correct way, and it splits the potential panic and breakages that usually comes with const generic PRs in two steps.

Other than the id, I think (at least I hope) other parts are in the right direction.
@Veykril
Copy link
Member Author

Veykril commented Dec 14, 2023

To summarize the current state of things, we have not taken the approach of using an expression store, but instead handling const args as separate bodies (called InTypeConsts), added in #14932. The implementation currently has some problems forcing us to basically having it disabled as it stands, namely storing the expected type of such a const arg and giving it an ID that is stable enough across random edits.

the expected type of such a const arg

We currently store the type (as a trait object due to the type being defined in hir-ty, while the container is defined in hir-def, a dependency of hir-ty) inside the interned location of the const arg. Interned things are supposed to be light weight though, and what's worse about this is if the expected type changes in some minor way, the hash changes meaning that the interned version will get a new slot allocated (without cleaning up the rpevious one, this is a salsa downside atm). It also just does not fit there from a architecture PoV.

Ideally we would have a query that calculates the expected type of a general const arg that could be used here then.

giving it an ID that is stable enough across random edits

This is the main reason why the current implementation is not usable, any edit across the file can invalidate all const args in the file currently as we merely use the const args AstId which becomes more unstable the deeper in the tree we go. Ideally this should be an ID relative to the owner of the const arg.

The expression store approach on the other hand trivially solves the stable ID issue (it would be relative to the store, like expression IDs are). It would also make const arg inference a bit less fine grained, instead of doing every const arg separately, we'd now be able to do it per store which I think is a general perf win, as body inference is already the finest inference unit we have anyways.

Unfortunately though, for current bodies we would have to split the expression store into two, one for the signature and one for the actual body, as otherwise we would lose lazyness when only being interested in the signature of an item, so this approach could have a noticable memory impact.

Regarding the expected type calculation, the query approach would be ideal here as well still, and I think more generally doable as t he expression store should already hold the required information for finding the relevant parts that are needed to lower the expected type of the AstId.

Generally I think I'd still be in favor of the expression store solution, as it seems like the robuster (albeit possibly more effort-requiring) way to implement this.

So with that said, the first step would be to extract the ExpressionStore part out of Body and BodySourceMap, and redefine Body and BodySourceMap on top of this new construct. Then we should start building ExpressionStores for relevant item signature and finally move the InTypeConst architecture over to this new ExpressionStore construct.

@Veykril
Copy link
Member Author

Veykril commented Jan 25, 2025

I've started looking into this again, and noticed some things.

As it turns out, this requires a bigger refactor. Basically we want to store ExprId in our TypeRef IR which is currently included in the ItemTree which is pre-expansion where as expression collection is post-expansion. Now, the ItemTree containing TypeRefs doesn't actually make sense in the first place. The ItemTree is supposed to be an incrementality later for the DefMap (our early name resolution), but types do not do anything on that level, so right now editing a parameter type will invalidate our early name resolution when it actually shouldn't! So the idea here is now to kick out everything from ItemTree that is not needed by the DefMap and create a new thing (ExpressionStore) that will serve as the incrementality layer for type holding items. This is a very rought explanation, the overall change is rather big as this touches upon a lot of core IRs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-Architecture Big architectural things which we need to figure up-front (or suggestions for rewrites :0) ) C-feature Category: feature request E-hard S-actionable Someone could pick this issue up and work on it right now
Projects
None yet
Development

No branches or pull requests

5 participants