Skip to content

Implement underscore lifetimes - #44691

Merged
bors merged 4 commits into
rust-lang:masterfrom
cramertj:underscore-lifetimes
Sep 22, 2017
Merged

Implement underscore lifetimes#44691
bors merged 4 commits into
rust-lang:masterfrom
cramertj:underscore-lifetimes

Conversation

@cramertj

Copy link
Copy Markdown
Member

Part of #44524

@rust-highfive

Copy link
Copy Markdown
Contributor

r? @nikomatsakis

(rust_highfive has picked a reviewer for you, use r? to override)

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Feel free to bikeshed this error message (it shows up in code that tries to explicitly bind '_ in the generics of an item declaration).

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.

An equivalent error is already issued for 'static, so the wording can be reused:

invalid lifetime parameter name: `'static`

@arielb1

Copy link
Copy Markdown
Contributor

r? @eddyb

Comment threadsrc/libsyntax/feature_gate.rs Outdated

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.

Should be 1.22.0? (BTW match_beginning_vert should also be 1.22.0?)

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 never know what to actually put here.

@eddyb

Copy link
Copy Markdown
Contributor

r=me with build & nit fixed

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.

Lifetime definitions can happen in for<'a, 'b, 'c> in fn pointers, trait objects, etc.
It's better to place the error in fn visit_lifetime_def.

@petrochenkov

Copy link
Copy Markdown
Contributor

@cramertj
What happens when '_ is written in some place where elided lifetimes are not supported currently?
For example bounds 'a: '_ + 'b, impl headers, struct definitions.
Could you add tests for these cases?

@cramertj
cramertjforce-pushed the underscore-lifetimes branch 2 times, most recently from 0c3b9ef to 942a85cCompareSeptember 19, 2017 18:11
Comment threadsrc/librustc/hir/mod.rs Outdated

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 I've never been crazy about using a "sentinel value" here rather than an enum -- now that we've got more than one sentinel, I feel even less good. Can't we change this to something like

enumLifetimeName{Elided,Underscore,Static,Name(Token)// or whatever this is}

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.

It's a Symbol / ast::Name I think.

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.

...if we were using an enum, this code would be rather cleaner

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.

can we have some tests that use multiple lifetime parameters in one function? For example:

fnfoo(x:&'_u8,y:&'_u8) -> &'_u8{}// ERROR from elisionfn foo(x:&mutVec<&'_u8>,y:&'_u8){ x.push(y);// ERROR }structFoo<'a,'b>{a:&'au32,b:&'bu32}fnfoo_a<'b>(foo:Foo<'_,'b>) -> &'bu32{&foo.a}// ERRORfnfoo_b<'b>(foo:Foo<'_,'b>) -> &'bu32{&foo.b}// OK

@nikomatsakis

Copy link
Copy Markdown
Contributor

@cramertj thanks for jumping on this btw -- so excited to see it land =)

@arielb1arielb1 added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Sep 19, 2017
Comment threadsrc/librustc/hir/mod.rs Outdated

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.

Nooooo.
This LifetimeName over Name is pure over-engineering, N extra lines of boilerplate just to avoid one string comparison.

@cramertjcramertjSep 19, 2017

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Heh. I don't have any strong opinion here. Take it up with @nikomatsakis 😄
I left it as its own commit so it's easy to pull out if that's the decision.

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'd better turn '_ into a weak keyword.

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 disagree. Sentinel values mean you wind up with if-else-if chains like if foo.is_elided() { ... } which are easily forgotten. Having an enum means we can use exhaustive matching, so that when we add variants in the future, the compiler helps us catch cases that are missing etc.

Plus, there isn't a lot of boilerplate here.

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.

@nikomatsakis
Ok, not a big deal.
(I'll revert this when you are on vacation, ha-ha.)

@cramertj

Copy link
Copy Markdown
MemberAuthor

Gah! I didn't try building rustdoc and the others...

@eddyb

Copy link
Copy Markdown
Contributor

cc @Mark-Simulacrum this is why I have rustbuild in my build setup.

Comment threadsrc/librustc/hir/mod.rs Outdated

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.

Symbol::intern("'static") => keywords::StaticLifetime.name()

@petrochenkov

Copy link
Copy Markdown
Contributor

@cramertj
Could you add tests for '_ in impl headers and struct definitions? (But not as bounds or lifetime definitions.)

@bors

bors commented Sep 21, 2017

Copy link
Copy Markdown
Collaborator

☔ The latest upstream changes (presumably #44551) made this pull request unmergeable. Please resolve the merge conflicts.

Comment threadsrc/librustc/hir/mod.rs Outdated

@petrochenkovpetrochenkovSep 21, 2017

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.

@cramertj
Actually, can you tweak the naming slightly.

enumLifetimeKind{// This is not a nameImplicit,// `Elided` is not correct, because implicit can be both elided and inferred, but we don't discern between them yet when lowering into HIRUnderscore,Static,Named(Name),// Named lifetime}

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.

(The variants are going to change anyway when lifetimes are first resolved, then lowered into HIR.)

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 think what we want is name resolution to give us a Def, and the existing resolve_lifetimes pass to do elision and compute the semantic representation from Def::Lifetime(DefId).

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.

@petrochenkov I don't object to calling "Implicit" for sure, but I'm curious: what does "elided" mean to you? To me, it means "not written". I would then say that the behavior around elided lifetimes depends on context: in function signatures, we use defaulting rules (as specified by the lifetime elision RFCs plus the object-lifetime RFCs), and in function bodies, we use inference.

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.

Using "elided" to mean literally "not written" may be correct wrt the English language but then "elision rules" doesn't mean much anymore and we have to say "default lifetime rules" I guess? And "lifetime defaulting" for the process of picking a lifetime.

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 personally, when I explain to people, I say something like "sometimes you can elide lifetimes. The meaning of this depends on context. In a function signature, we use the following system of defaults. etc". In other words, I think I do use the terms the way you described. =) But also I rely on context. That is, I might say "elision rules" when it's clear we're talking about function signatures, in which case it just means "defaulting rules" (but including, naturally, the object defaults).

In any case, it seems clear that calling things elided is ripe for confusion. I prefer either "implicit", "not written", or "not present", all of which seem reasonably clear.

@petrochenkovpetrochenkovSep 21, 2017

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.

@nikomatsakis
Well, I usually associate "elided" with "lifetime elision RFCs", i.e. "defaulting", not inference.
It's useful to have some separate words for "not-written-in-signature" and "not-written-in-body", and "elided" and "inferred" already kinda serve this purpose given that "lifetime elision" is commonly used to describe what happens in signatures.

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.

That makes some sense. I guess I prefer to have words that match their English meanings to the extent possible, though, and to me "elided" just means "not written" (whereas "defaulted" seems more specific -- we assigned it a default value). I admit that the distinction between defaulted (based on some simple rules) and inferred (based on complex analysis) is a subtle one as well. =)

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.

Regardless, the question at hand is whether to use elided here with the broader meaning, right? In which case, "Implicit" or "Not written" seems fine.

@nikomatsakis

Copy link
Copy Markdown
Contributor

@bors r+

@bors

bors commented Sep 21, 2017

Copy link
Copy Markdown
Collaborator

📌 Commit f5505d1 has been approved by nikomatsakis

@nikomatsakisnikomatsakis added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Sep 21, 2017
@bors

bors commented Sep 22, 2017

Copy link
Copy Markdown
Collaborator

⌛ Testing commit f5505d1 with merge 3eb19bf...

bors added a commit that referenced this pull request Sep 22, 2017
@bors

bors commented Sep 22, 2017

Copy link
Copy Markdown
Collaborator

☀️ Test successful - status-appveyor, status-travis
Approved by: nikomatsakis
Pushing 3eb19bf to master...

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

Labels

S-waiting-on-borsStatus: Waiting on bors to run and complete tests. Bors will change the label on completion.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants

@cramertj@rust-highfive@arielb1@eddyb@petrochenkov@nikomatsakis@bors@kennytm