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

Implement underscore lifetimes #44691

Merged
merged 4 commits into from Sep 22, 2017

Conversation

Projects
None yet
8 participants
@cramertj
Copy link
Member

cramertj commented Sep 19, 2017

Part of #44524

@rust-highfive

This comment has been minimized.

Copy link
Collaborator

rust-highfive commented Sep 19, 2017

r? @nikomatsakis

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

self.err_handler()
.span_err(lt_def.lifetime.span,
&format!("invalid lifetime name `{}`", lt_def.lifetime.ident));
}

This comment has been minimized.

@cramertj

cramertj Sep 19, 2017

Author Member

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

This comment has been minimized.

@petrochenkov

petrochenkov Sep 19, 2017

Contributor

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

invalid lifetime parameter name: `'static`
@arielb1

This comment has been minimized.

Copy link
Contributor

arielb1 commented Sep 19, 2017

r? @eddyb

@@ -383,6 +383,9 @@ declare_features! (

// allow '|' at beginning of match arms (RFC 1925)
(active, match_beginning_vert, "1.21.0", Some(44101)),

// allow `'_` placeholder lifetimes
(active, underscore_lifetimes, "1.21.0", Some(44524)),

This comment has been minimized.

@kennytm

kennytm Sep 19, 2017

Member

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

This comment has been minimized.

@eddyb

eddyb Sep 19, 2017

Member

I never know what to actually put here.

@eddyb

This comment has been minimized.

Copy link
Member

eddyb commented Sep 19, 2017

r=me with build & nit fixed

@@ -318,6 +310,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {

fn visit_generics(&mut self, g: &'a Generics) {
let mut seen_default = None;
for lt_def in &g.lifetimes {

This comment has been minimized.

@petrochenkov

petrochenkov Sep 19, 2017

Contributor

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

This comment has been minimized.

Copy link
Contributor

petrochenkov commented Sep 19, 2017

@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 cramertj force-pushed the cramertj:underscore-lifetimes branch 2 times, most recently from 0c3b9ef to 942a85c Sep 19, 2017

@@ -159,7 +159,8 @@ impl fmt::Debug for Lifetime {

impl Lifetime {
pub fn is_elided(&self) -> bool {
self.name == keywords::Invalid.name()
self.name == keywords::Invalid.name() ||
self.name == "'_"

This comment has been minimized.

@nikomatsakis

nikomatsakis Sep 19, 2017

Contributor

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

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

This comment has been minimized.

@eddyb

eddyb Sep 19, 2017

Member

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

@@ -1422,7 +1422,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
let lifetime_i = &lifetimes[i];

for lifetime in lifetimes {
if lifetime.lifetime.is_static() {
if lifetime.lifetime.is_static() || lifetime.lifetime.name == "'_" {

This comment has been minimized.

@nikomatsakis

nikomatsakis Sep 19, 2017

Contributor

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


struct Foo<'a>(&'a u8);

fn foo(x: &u8) -> Foo<'_> {

This comment has been minimized.

@nikomatsakis

nikomatsakis Sep 19, 2017

Contributor

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

fn foo(x: &'_ u8, y: &'_ u8) -> &'_ u8 { } // ERROR from elision

fn foo(x: &mut Vec<&'_ u8>, y: &'_ u8) { x.push(y); // ERROR }

struct Foo<'a, 'b> { a: &'a u32, b: &'b u32 }
fn foo_a<'b>(foo: Foo<'_, 'b>) -> &'b u32 { &foo.a } // ERROR
fn foo_b<'b>(foo: Foo<'_, 'b>) -> &'b u32 { &foo.b } // OK
@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Sep 19, 2017

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

@cramertj cramertj force-pushed the cramertj:underscore-lifetimes branch from ac16bad to c91a1a7 Sep 19, 2017

Elided => keywords::Invalid.name(),
Underscore => Symbol::intern("'_"),
Static => Symbol::intern("'static"),
Name(name) => name,

This comment has been minimized.

@petrochenkov

petrochenkov Sep 19, 2017

Contributor

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

This comment has been minimized.

@cramertj

cramertj Sep 19, 2017

Author Member

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.

This comment has been minimized.

@petrochenkov

petrochenkov Sep 19, 2017

Contributor

I'd better turn '_ into a weak keyword.

This comment has been minimized.

@nikomatsakis

nikomatsakis Sep 20, 2017

Contributor

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.

This comment has been minimized.

@petrochenkov

petrochenkov Sep 20, 2017

Contributor

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

@cramertj

This comment has been minimized.

Copy link
Member Author

cramertj commented Sep 20, 2017

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

@eddyb

This comment has been minimized.

Copy link
Member

eddyb commented Sep 20, 2017

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

@cramertj cramertj force-pushed the cramertj:underscore-lifetimes branch from c91a1a7 to 051b5cf Sep 20, 2017

match *self {
Elided => keywords::Invalid.name(),
Underscore => Symbol::intern("'_"),
Static => Symbol::intern("'static"),

This comment has been minimized.

@petrochenkov

petrochenkov Sep 20, 2017

Contributor

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

@cramertj cramertj force-pushed the cramertj:underscore-lifetimes branch from 051b5cf to 71c8ad4 Sep 20, 2017

@petrochenkov

This comment has been minimized.

Copy link
Contributor

petrochenkov commented Sep 20, 2017

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

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Sep 21, 2017

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

@cramertj cramertj force-pushed the cramertj:underscore-lifetimes branch from 8f5f35c to 31fd099 Sep 21, 2017

Underscore,
Static,
Name(Name),
}

This comment has been minimized.

@petrochenkov

petrochenkov Sep 21, 2017

Contributor

@cramertj
Actually, can you tweak the naming slightly.

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

This comment has been minimized.

@petrochenkov

petrochenkov Sep 21, 2017

Contributor

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

This comment has been minimized.

@eddyb

eddyb Sep 21, 2017

Member

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).

This comment has been minimized.

@nikomatsakis

nikomatsakis Sep 21, 2017

Contributor

@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.

This comment has been minimized.

@eddyb

eddyb Sep 21, 2017

Member

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.

This comment has been minimized.

@nikomatsakis

nikomatsakis Sep 21, 2017

Contributor

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.

This comment has been minimized.

@petrochenkov

petrochenkov Sep 21, 2017

Contributor

@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.

This comment has been minimized.

@nikomatsakis

nikomatsakis Sep 21, 2017

Contributor

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. =)

This comment has been minimized.

@nikomatsakis

nikomatsakis Sep 21, 2017

Contributor

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.

@cramertj cramertj force-pushed the cramertj:underscore-lifetimes branch from 31fd099 to f5505d1 Sep 21, 2017

@nikomatsakis

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Sep 21, 2017

@bors r+

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Sep 21, 2017

📌 Commit f5505d1 has been approved by nikomatsakis

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Sep 22, 2017

⌛️ Testing commit f5505d1 with merge 3eb19bf...

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

Auto merge of #44691 - cramertj:underscore-lifetimes, r=nikomatsakis
Implement underscore lifetimes

Part of #44524
@bors

This comment has been minimized.

Copy link
Contributor

bors commented Sep 22, 2017

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

@bors bors merged commit f5505d1 into rust-lang:master Sep 22, 2017

2 checks passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
homu Test successful
Details

@cramertj cramertj deleted the cramertj:underscore-lifetimes branch Sep 22, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.