Skip to content

internal: implement lifetime elision - #22927

Open
dfireBird wants to merge 2 commits into
rust-lang:masterfrom
dfireBird:lifetime_elision
Open

internal: implement lifetime elision#22927
dfireBird wants to merge 2 commits into
rust-lang:masterfrom
dfireBird:lifetime_elision

Conversation

@dfireBird

Copy link
Copy Markdown
Member

No description provided.

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jul 27, 2026
@dfireBird

Copy link
Copy Markdown
Member Author

Not sure why this fails in CI and passes in my system?

@ShoyuVanilla

ShoyuVanilla commented Jul 27, 2026

Copy link
Copy Markdown
Member

Not sure why this fails in CI and passes in my system?

Use `env RUN_SLOW_TESTS=1 cargo test` to run the full suite.

You need to set an extra env var to run slow tests

@dfireBird

Copy link
Copy Markdown
Member Author

Oh I didn't know, we ran slow tests. My bad 😅

@Veykril Veykril left a comment

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.

Only a partial review so far

View changes since this review

Comment on lines +1332 to +1333
if let Some(binder) = &fn_.binder
&& !binder.is_empty()

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.

nit, lets drop the is_empty check. Ideally we'd just lower to None when binder is empty

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah I guess, I should've kept None if the binder is empty after fn ptr lowering instead of this and that makes sense as well.

Comment thread crates/hir-def/src/lib.rs
pub scope: GenericDefId,
pub local_id: usize,
}
pub struct HrtbLifetimeParamId(pub usize);

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
pub struct HrtbLifetimeParamId(pub usize);
pub struct HrtbLifetimeParamId(pub u32);

We wont ever have that many lifetimes, so being conservative for memory usage won't hurt here

LifetimeRef::Param(lifetime_param_id) => {
Some(LifetimeNs::LifetimeParam(*lifetime_param_id))
}
LifetimeRef::HrtbParam(_) => None, // this should be unreachable, should we panic?

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
LifetimeRef::HrtbParam(_) => None, // this should be unreachable, should we panic?
LifetimeRef::HrtbParam(_) => {
never!();
None
}

TypeRef::Fn(fn_) => {
if let Some(binder) = &fn_.binder {
if let Some(binder) = &fn_.binder
&& !binder.is_empty()

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.

same here

fn bar(x: &u32) {}
"#,
expect!["ty: &'_ &'_ u32, name: x"],
expect!["ty: &'_ &'<erased> u32, name: x"],

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.

we probably need to check our rendering now, we shouldn't show erased lifetimes to the user

@dfireBird dfireBird Jul 27, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I could make the RegionKind::ReErased to display '_ for now, would that be fine?

Comment on lines +2675 to +2676
lc world &WorldSnapshot [type_could_unify+name+local]
ex world [type_could_unify]

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.

Interesting, so we lose a lot of type equality now which I guess makes sense. Worth to keep in mind if we want t o have something like type-equal module lifetimes

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.

We should definitely not render '_ in the generic param list 🙈 if we can't yet render the elided lifetimes nicely here we should probably hide them wholesale

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Would it be fine, if we still showed them in the argument list?

Comment thread crates/hir-def/src/expr_store/lower/generics.rs
@Veykril

Veykril commented Jul 27, 2026

Copy link
Copy Markdown
Member

Very excited for this, I've been wanting this for so long!

@ChayimFriedman2 ChayimFriedman2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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


```rust
pub fn foo(_: &Path)
pub fn foo<'_>(_: &'_ Path)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

IMO this is worse display. I think we should omit '_ generics, and omit them for references as well. For non-references - I'm not sure.

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.

Agree'd, this should mimic the lint. So rendering '_ for paths is good, for references we should skip as its always implied.

@dfireBird dfireBird Jul 29, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think it comes down to what I was saying before in zulip chat #t-compiler/rust-analyzer > Lifetime elision @ 💬, have a way to determine if a LifetimeRef is elided or not, provided we have ExpressionStore.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You could check the name for '_, or add another way to differentiate.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I could indeed check for LifetimeRef::Param, but if it's going to be used in other places, it's going to be a bit of problem.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

But only elided lifetimes can have name '_.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh I realized something that I can do, I'll try that.


#[inline]
pub fn anon_lifetime() -> Name {
Self::new_text("'_")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This should be a predefined symbol.

Comment thread crates/hir-def/src/lib.rs
ModuleDefId::StaticId(id) => Some(id.into()),
ModuleDefId::TraitId(id) => Some(id.into()),
ModuleDefId::TypeAliasId(id) => Some(id.into()),
_ => None,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Use exhaustive matching here.

self.lower_type_ref(
it,
impl_trait_lower_fn,
&mut Self::elided_lifetime_placeholder_allocator,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This does not look correct. We should first lower the arguments. If there is exactly one HRTB lifetime in them, any elided lifetime in the return type should refer to it. Otherwise, it's an error lifetime (plus a diagnostic). In fact, I'm pretty sure we should remove LifetimeRef::Placeholder.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I use LifetimeRef::Placeholder for return types, so that I can resolve it correctly during hir-ty lowering. But I missed to copy from my old WIP of the lifetime elision.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I see no reason to resolve it during hir-ty lowering and complicate the code with additional variant when we can resolve it during hir lowering instead.

&mut self,
node: ast::Type,
impl_trait_lower_fn: ImplTraitLowerFn<'_>,
lifetime_elision_fn: LifetimeElisionFn<'_>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not pleased with passing another callback everywhere. I would prefer an enum field saying what to do: an error, a 'static lifetime (for const and static types), a specific lifetime, or a new anonymous lifetime. In fact you can look at the old LifetimeElisionKind, which implements this (but only for diagnostics) and was copied from rustc.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I would anyways need access to lifetime Arena in GenericParamCollector.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What do you mean?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The callback works by pushing a '_ and using that Idx to create the LifetimeParamId.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So? Why can't this be done via an enum field?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

You mean to say use a mutable reference of the arena inside one of enum variant.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't understand what you mean.

What I'm proposing is, instead of accepting a callback for what to do when encountering an elided lifetime, keep a field lifetime_elision_kind of type LifetimeElisionKind in ExpressionStore, similar to (in fact replacing) LifetimeElisionKind in TyLoweringContext, and act according to it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The primary purpose of it being a callback here is to have a mutable access to the lifetime arena, but otherwise, I can indeed do what you suggest.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm, I think it will be better if the enum LifetimeElisionKind will have a variant AsLifetimeParam { collector: &mut GenericParamsCollector, ... }. This does mean introducing another lifetime to ExprCollector, but that's not so bad.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah, I can do that. Let me do it along with some other review comments over the weekend..

{
TypeBound::ForLifetime(binder, path)
} else {
TypeBound::Path(path, m)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we even need TypeBound::Path now? After all in rustc_type_ir every predicate has a binder. We could just always produce ForLifetime.

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

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants