-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Remove kw::Empty
uses from hir::Lifetime::ident
#138965
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
Remove kw::Empty
uses from hir::Lifetime::ident
#138965
Conversation
Best reviewed one commit at a time. |
hir::LifetimeName::Infer | ||
} | ||
LifetimeRes::Static { .. } => { | ||
debug_assert!(matches!(ident.name, kw::StaticLifetime | kw::UnderscoreLifetime)); |
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.
when does this happen to be an underscore lifetime?
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.
When the 'static
is elided, e.g. for both C
and D
here:
struct Name<'a>(&'a str);
const C: Name = Name("name"); // IsAnonInPath::Yes
static D: &str = ""; // IsAnonInPath::No
I didn't even realize 'static
could be elided like this!
I will add a comment about this.
hir::LifetimeName::ImplicitObjectLifetimeDefault, | ||
IsPathAnon::No, |
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.
would it be easier to have a LifetimeName::Anon|ElidedInPath
? and not have two enums inside of hir::Lifetime
?
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 originally wanted to combine the two enums in exactly this fashion, but it turns out that IsAnonInPath::Yes
can occur with four of the five LifetimeName
variants, viz:
#![allow(unused)]
//---------------------------------------------------------------------------
struct Name<'a>(&'a str);
const C: Name = Name("name"); // LifetimeRes::Static, name='_, IsAnonInPath::Yes
static D: &str = ""; // LifetimeRes::Static, name='_, IsAnonInPath::No
//---------------------------------------------------------------------------
#[repr(C)]
struct Safe<'a>(&'a u32);
extern "C" {
fn foo(safe: Safe); // LifetimeRes::Param, name='_, IsAnonInPath::Yes
}
//---------------------------------------------------------------------------
struct St<'a> { f1: &'a u32 }
fn f() {
let st = St { f1: &0u32 }; // LifetimeRes::Infer, name='_, IsAnonInPath::Yes
}
//---------------------------------------------------------------------------
// commented out because it causes an error
//struct Baz<'a>(&'a str); // LifetimeRes::Error, name='_, IsAnonInPath::Yes
//struct Baz2(Baz);
fn main() {}
Once again, lifetimes can be omitted from paths in more places than I realized.
If you wanted to combine ident
and res
and is_anon_in_path
you'd have an enum like this:
enum Combined {
ParamUserNamed(Ident), // user-specified name, IsAnonInPath::No
ParamAnon(Span, IsAnonInPath), // name='_
Infer(Span, IsAnonInPath), // name='_
StaticNamed(Span), // name='static, IsAnonInPath::No
StaticAnon(IsAnonInPath), // name='_
Error(Ident), // user-specified name, IsAnonInPath::No
ErrorAnon(Span, IsAnonInPath), // name='_
ImplicitObjectLifetimeDefault, // name='_, IsAnonInPath::No
}
I think that captures all the possibilities and excludes all invalid combinations. I'm generally very much a fan of "don't let your types express invalid values" but this feels like it's taking it too far?
99843d6
to
4f1b408
Compare
I updated, making these changes:
|
This comment has been minimized.
This comment has been minimized.
4f1b408
to
26bc215
Compare
compiler/rustc_hir/src/hir.rs
Outdated
/// const A: Name = Name("a"); // LifetimeName::Static, name='_, IsAnonInPath::Yes | ||
/// const B: &str = ""; // LifetimeName::Static, name='_, IsAnonInPath::No |
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.
so this PR doesn't change behavior which is fine, however. Even with this cleanup the status quo still feels confusing to me.
I feel like maybe Lifetime
should be something like
struct Lifetime {
hir_id: HirId,
source: LifetimeSource,
res: LifetimeName,
}
enum LifetimeSource {
// Used for everything where we have an explicit lifetime, including `'_`
Explicit { name: Ident },
// Used for fully elided lifetimes, i.e. all cases of `IsAnonInPath::Yes`
// as well as `&Type` maybe. Idk whether these should be treated differently.
FullyElided { insert_span: Span },
}
Do you think this approach would make sense? I think merging this PR as is is already an improvement as it makes the weirdness more explicit.
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.
// Used for fully elided lifetimes, i.e. all cases of `IsAnonInPath::Yes`
// as well as `&Type` maybe. Idk whether these should be treated differently.
FullyElided { insert_span: Span },
The "as well as &Type
maybe" isn't possible. The IsAnonInPath
has to be distinct otherwise LifetimeSuggestion
won't work. You could do this:
pub enum LifetimeIdent {
Normal { ident: Ident },
IsAnonInPath { span: Span },
}
It would avoid the invalid combination of IsAnonInPath::Yes
with a non-underscore lifetime ident, but that's the only benefit, and it adds a bunch of match
es, etc. I don't think it's worth it.
There might still be a nicer formulation of this code, but I think this PR is worth landing in its current state.
if ident.name == kw::Empty && ident.span.is_empty() { | ||
let make_suggestion = |lifetime: &hir::Lifetime| { | ||
if lifetime.is_anon_in_path == IsAnonInPath::Yes | ||
&& lifetime.ident.span.is_empty() |
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.
when is this part of the condition ever false. I would expect all AnonInPath
to have empty spans
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.
IsAnonInPath::Yes
lifetimes are inserted in maybe_insert_elided_lifetimes_in_path
. In that function elided_lifetime_span
can definitely be non-empty.
However, none of those non-empty spans reach here, at least when running the UI tests. (I checked by adding an assertion.) I don't know if that's guaranteed, or if we just have insufficient test coverage. So I'd prefer to keep the test here for now; it is retained from the original code.
} else if lifetime.ident.name == kw::UnderscoreLifetime | ||
&& lifetime.ident.span.is_empty() |
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 is where changing ident
to a source enum would be helpful
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.
a suggestion and one final nit
r=me after fixing the nit if u don't apply the suggestion
HIR printing currently gets very little testing. This increases coverage a bit, with a focus on lifetimes. There are some FIXME comments for cases that are printed in a dubious fashion. This PR won't address those; the point of adding this test is to ensure that the subsequent commits don't hurt pretty-printing.
They both are only used in `Lifetime::suggestion`. This commit inlines and removes them.
It has no effect on anything in the test suite. This means it can also be rewritten as a neater pairwise `match`.
`hir::Lifetime::ident` currently sometimes uses `kw::Empty` for elided lifetimes and sometimes uses `kw::UnderscoreLifetime`, and the distinction is used when creating some error suggestions, e.g. in `Lifetime::suggestion` and `ImplicitLifetimeFinder::visit_ty`. I found this *really* confusing, and it took me a while to understand what was going on. This commit replaces all uses of `kw::Empty` in `hir::Lifetime::ident` with `kw::UnderscoreLifetime`. It adds a new field `hir::Lifetime::is_path_anon` that mostly replaces the old empty/underscore distinction and makes things much clearer. Some other notable changes: - Adds a big comment to `Lifetime` talking about permissable field values. - Adds some assertions in `new_named_lifetime` about what ident values are permissible for the different `LifetimeRes` values. - Adds a `Lifetime::new` constructor that does some checking to make sure the `is_elided` and `is_anonymous` states are valid. - `add_static_impl_trait_suggestion` now looks at `Lifetime::res` instead of the ident when creating the suggestion. This is the one case where `is_path_anon` doesn't replace the old empty/underscore distinction. - A couple of minor pretty-printing improvements.
26bc215
to
8d2c63f
Compare
I didn't change the @bors r=lcnr |
☀️ Test successful - checks-actions |
What is this?This is an experimental post-merge analysis report that shows differences in test outcomes between the merged PR and its parent PR.Comparing 7586a9f (parent) -> 3f690c2 (this PR) Test differencesShow 17 test diffsStage 1
Stage 2
Additionally, 15 doctest diffs were found. These are ignored, as they are noisy. Job group index
Job duration changes
How to interpret the job duration changes?Job durations can vary a lot, based on the actual runner instance |
Finished benchmarking commit (3f690c2): comparison URL. Overall result: no relevant changes - no action needed@rustbot label: -perf-regression Instruction countThis benchmark run did not return any relevant results for this metric. Max RSS (memory usage)Results (secondary -2.1%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResults (secondary -0.3%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 778.708s -> 777.401s (-0.17%) |
Kindly have a look at #138677 and let me know if you think I’ll have to undo (some of?) these changes. |
@shepmaster: Oof, that's unfortunate timing, me removing the previously-unused distinction between
|
I don't believe that will be required. I've rebased on top of your changes and pushed on through. That is my entire set of change, which will be spread out over at least three PRs (two of which are open now) — only the first 4 commits will exist in PR #138677. Specifically related to these changes, I've continued modifying
I'm not totally sure what you'd expect, but hopefully the reworked version of that function reads more cleanly?
They were deleted in this very PR 😉
When I asked for help on Zulip, it was suggested that a HIR lint would be nicer. Based on that feedback, I scrapped my original lint changes which lived in rustc_resolve and rewrote them (extending them based on lang-team feedback) as acting on HIR.
I think there's some strong similarity to my changes there. Notably, I see you are tracking if there are angle brackets, which is also something I needed to add.
My reworked code continues to eliminate the the closure as well. I'm quite happy that duplicated code is being removed. |
Oh, great news. I will stop working on #139046 until you are finished with the lint, though feel free to steal any ideas from it that work for you :)
lol, I totally forgot I had done that. Sorry for the confusion. |
hir::Lifetime::ident
is sometimes set tokw::Empty
and it's really confusing. This PR stops that. Helps with #137978.r? @lcnr