Skip to content

Instantly share code, notes, and snippets.

@nikomatsakis
Last active November 18, 2017 02:23
Show Gist options
  • Save nikomatsakis/bb79258eb97563db0ef3818d3686187c to your computer and use it in GitHub Desktop.
Save nikomatsakis/bb79258eb97563db0ef3818d3686187c to your computer and use it in GitHub Desktop.

Here are some scenarios to test. Many of the tests include errors. I think I'd prefer to see these as UI tests, so that we can see the error output, though those often work best if we break them up into small files, each of which should either pass or produce one error (at least until the extensions are added to allow //~ ERROR in ui tests).


Within a function.

fn foo(x: &'a u32, y: &u32) -> &'a u32 { x }
// Check that in-band can be late-bound.
fn foo(x: &'a u32, y: &u32) -> &'a u32 { x }

fn main() {
  let x: for<'x> fn(&'x u32, &u32) -> &'x u32 = foo;
}
// Check that in-band can be early-bound.
fn foo(x: &'a u32, y: &u32) -> &'a u32
where 'static: 'a
{ x }

fn main() {
  let x: for<'x> fn(&'x u32, &u32) -> &'x u32 = foo; //~ ERROR
}

And some negative tests:

fn foo(x: &'a u32, y: &u32) -> &'a u32 { x }
fn main() {
    let mut p = 3;
    let r = foo(&p);
    p += 1; //~ ERROR
    println!("{}", r);
}
fn foo(x: &'a u32, y: &u32) -> &'a u32 { y } //~ ERROR
fn foo(x: &'a u32, y: &'b u32) -> &'a u32 { y } //~ ERROR

Within an inherent method.

// Test that we can use an in-band lifetime
// purely local to an inherent method.

struct Foo;

impl Foo {
  fn foo(x: &'a u32, y: &u32) -> &'a u32 { x }
}
// Test that we can use an in-band lifetime
// `'foo` to link between `self` and the parameter
// `x`.

struct Foo<'foo> { x: &'foo u32 };

impl Foo<'foo> {
  // Here `x` has same lifetime as `self.x`.
  fn foo(&mut self, x: &'foo u32) {
    self.x = x; // OK
  }
  
  // Here, there is no link, so we expect an 
  // error.
  fn bar(&mut self, x: &u32) {
    self.x = x; //~ ERROR
  }
}

// Test that if we call `foo` with an
// inappropriate value we get an error.
fn call_foo(f: &mut Foo<'static>) {
  f.foo(&22); //~ ERROR
}

// But `bar` can be called with any lifetime.
fn call_bar(f: &mut Foo<'static>) {
  f.foo(&22); // OK
}

Within a trait method.

// Test use of an in-band lifetime purely
// local to a trait method, both with and
// without default.

trait Get {
  fn foo(&self, x: &'a u32, y: &u32) -> &'a u32;
  
  fn bar(&self, x: &'a u32, y: &u32) -> &'a u32 {
    x // OK
  }

  fn baz(&self, x: &'a u32, y: &u32) -> &'a u32 {
    y //~ ERROR
  }
}

Within a trait impl

// Test use of an in-band lifetime within an
// impl of a trait.

trait Get {
  fn foo(&self, x: &'a u32, y: &u32) -> &'a u32;
}

// Test implementing a trait that used in-band with
// an impl that also does.
impl Get for () {
  fn foo(&self, x: &'a u32, y: &u32) -> &'a u32 {
    x // OK
  }
}

// Test implementing a trait that used in-band with
// an impl that also does, but where impl is wrong.
impl Get for i32 {
  fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 {
    //~^ ERROR
    x
  }
}

// Test implementing a trait that used in-band with
// an impl that does not.
//
// This should probably work, given our current flexibility
// around lifetimes.
impl Get for u32 {
  fn foo<'b>(&self, x: &'b u32, y: &u32) -> &'b u32 {
    x
  }
}
// Test use of an in-band lifetime within an
// impl of a trait.

trait Get {
  fn foo<'a>(&self, x: &'a u32, y: &u32) -> &'a u32;
}

// Test implementing a trait that did not use in-band
// lifetimes with an impl that does.
impl Get for () {
  fn foo(&self, x: &'a u32, y: &u32) -> &'a u32 {
    x
  }
}

Higher-ranked types.

The RFC states:

For higher-ranked types (including cases like Fn syntax), elision works as it does today. However, it is an error to mention a lifetime in a higher-ranked type that hasn't been explicitly bound (either at the outer fn definition, or within an explicit for<>). These cases are extremely rare, and making them an error keeps our options open for providing an interpretation later on.

This seems a bit stricter than I would like, but that's what the text says. This clearly implies that the following are errors:

fn foo(x: fn(&'a u32)) { //~ ERROR `'a` not explicitly bound
}

fn bar(x: &dyn Fn(&'a u32)) { //~ ERROR `'a` not explicitly bound
}

fn baz(x: impl Fn(&'a u32)) { //~ ERROR `'a` not explicitly bound
}

The text as written implies that the following two examples are in error too, though I think I wish that they were not:

fn foo(x: fn(&'a u32), y: &'a u32) { //~ ERROR `'a` not explicitly bound
}
struct Foo<'a> { x: &'a u32 }
impl Foo<'a> {
  fn bar(&self, x: fn(&'a u32)) {
    //~ ERROR `'a` not explicitly bound
  }
}

Structs and other types

No in-band lifetimes may be introduced in structs or other types

struct Foo {
  x: &'test u32 //~ ERROR
}

enum Bar {
  Baz(&'test u32)  //~ ERROR
}

Local variables

No in-band lifetimes may be introduced in local variables.

fn foo(x: &u32) {
  let y: &'test u32 = x; //~ ERROR
}

But in-band lifetimes may be referenced from local variables.

fn foo(x: &'test u32) -> &'test u32 {
  let y: &'test u32 = x; // OK
  y
}

Note that local variables can also not introduce in-band lifetimes in higher-ranked types:

fn foo(x: &u32) { }
fn bar() {
  let y: fn(&'test u32) = foo; //~ ERROR 'test not declared
}

Intermingled

The RFC states:

If any lifetime names are explicitly bound, they all must be.

i.e., if there are any explicit lifetime decls, then you cannot use the in-band kind. So for example:

fn foo<'a>(x: &'a u32, y: &'b u32) { } //~ ERROR: 'b

I would however expect you to be able to use in-band names already introduced:

struct Foo<'a> { x: &'a u32 }

impl Foo<'a> {
  fn bar<'b>(x: &'a u32, y: &'b u32) { } // OK to use 'a and 'b here
}

But not add new ones:

struct Foo<'a> { x: &'a u32 }

impl Foo<'a> {
  fn bar<'b>(x: &'a u32, y: &'b u32, z: &'c u32) { } //~ ERROR 'c
}

Shadowed

Out-of-band should not shadow in-band (not explicitly stated, but seems consistent with our existing rules):

struct Foo<'s> { }
impl Foo<'s> {
  fn bar<'s>(...) //~ ERROR shadows 's
}
struct Foo<'s> { }
impl Foo<'s> {
  fn bar(x: for<'s> fn(&'s u32)) //~ ERROR shadows 's
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment