about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2019-08-08 03:36:24 -0400
committerNiko Matsakis <niko@alum.mit.edu>2019-08-19 13:50:42 -0400
commitaf86fb1959b520ae0256272899df5c43b11df2a7 (patch)
treec1a076002aabb33c37bc366694a4dc940addce24
parentb51df1def0e621fd5fd6cb777511d64d490c0363 (diff)
downloadrust-af86fb1959b520ae0256272899df5c43b11df2a7.tar.gz
rust-af86fb1959b520ae0256272899df5c43b11df2a7.zip
distinguish object-lifetime-default elision from other elision
Object-lifetime-default elision is distinct from other forms of
elision; it always refers to some enclosing lifetime *present in the
surrounding type* (e.g., `&dyn Bar` expands to `&'a (dyn Bar + 'a)`.
If there is no enclosing lifetime, then it expands to `'static`.

Therefore, in an `impl Trait<Item = dyn Bar>` setting, we don't expand
to create a lifetime parameter for the `dyn Bar + 'X` bound.  It will
just be resolved to `'static`.

Annoyingly, the responsibility for this resolution is spread across
multiple bits of code right now (`middle::resolve_lifetimes`,
`lowering`). The lowering code knows that the default is for an object
lifetime, but it doesn't know what the correct result would be.
Probably this should be fixed, but what we do now is a surgical fix:
we have it generate a different result for elided lifetimes in a
object context, and then we can ignore those results when figuring out
the lifetimes that are captured in the opaque type.
-rw-r--r--src/librustc/hir/intravisit.rs1
-rw-r--r--src/librustc/hir/lowering.rs19
-rw-r--r--src/librustc/hir/mod.rs21
-rw-r--r--src/librustc/middle/resolve_lifetime.rs17
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs3
-rw-r--r--src/test/ui/async-await/issues/issue-62517-1.rs23
-rw-r--r--src/test/ui/async-await/issues/issue-62517-2.rs16
-rw-r--r--src/test/ui/async-await/issues/issue-62517-2.stderr11
-rw-r--r--src/test/ui/impl-trait/dyn-trait-elided-two-inputs-assoc.rs16
-rw-r--r--src/test/ui/impl-trait/dyn-trait-elided-two-inputs-param.rs11
-rw-r--r--src/test/ui/impl-trait/dyn-trait-elided-two-inputs-ref-param.rs23
-rw-r--r--src/test/ui/lifetimes/lifetime-elision-return-type-trait.rs2
-rw-r--r--src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr10
13 files changed, 163 insertions, 10 deletions
diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index 2c6373bdfa4..fa274f831b7 100644
--- a/src/librustc/hir/intravisit.rs
+++ b/src/librustc/hir/intravisit.rs
@@ -433,6 +433,7 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime
         LifetimeName::Static |
         LifetimeName::Error |
         LifetimeName::Implicit |
+        LifetimeName::ImplicitObjectLifetimeDefault |
         LifetimeName::Underscore => {}
     }
 }
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index cd127177e9f..e04e45e5fbc 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -1560,6 +1560,11 @@ impl<'a> LoweringContext<'a> {
                         }
                     }
                     hir::LifetimeName::Param(_) => lifetime.name,
+
+                    // Refers to some other lifetime that is "in
+                    // scope" within the type.
+                    hir::LifetimeName::ImplicitObjectLifetimeDefault => return,
+
                     hir::LifetimeName::Error | hir::LifetimeName::Static => return,
                 };
 
@@ -2550,6 +2555,12 @@ impl<'a> LoweringContext<'a> {
                     hir::LifetimeName::Implicit
                         | hir::LifetimeName::Underscore
                         | hir::LifetimeName::Static => hir::ParamName::Plain(lt.name.ident()),
+                    hir::LifetimeName::ImplicitObjectLifetimeDefault => {
+                        span_bug!(
+                            param.ident.span,
+                            "object-lifetime-default should not occur here",
+                        );
+                    }
                     hir::LifetimeName::Error => ParamName::Error,
                 };
 
@@ -3293,7 +3304,13 @@ impl<'a> LoweringContext<'a> {
             AnonymousLifetimeMode::PassThrough => {}
         }
 
-        self.new_implicit_lifetime(span)
+        let r = hir::Lifetime {
+            hir_id: self.next_id(),
+            span,
+            name: hir::LifetimeName::ImplicitObjectLifetimeDefault,
+        };
+        debug!("elided_dyn_bound: r={:?}", r);
+        r
     }
 
     fn new_implicit_lifetime(&mut self, span: Span) -> hir::Lifetime {
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index b469b7016be..98304818852 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -221,6 +221,19 @@ pub enum LifetimeName {
     /// User wrote nothing (e.g., the lifetime in `&u32`).
     Implicit,
 
+    /// Implicit lifetime in a context like `dyn Foo`. This is
+    /// distinguished from implicit lifetimes elsewhere because the
+    /// lifetime that they default to must appear elsewhere within the
+    /// enclosing type.  This means that, in an `impl Trait` context, we
+    /// don't have to create a parameter for them. That is, `impl
+    /// Trait<Item = &u32>` expands to an opaque type like `type
+    /// Foo<'a> = impl Trait<Item = &'a u32>`, but `impl Trait<item =
+    /// dyn Bar>` expands to `type Foo = impl Trait<Item = dyn Bar +
+    /// 'static>`. The latter uses `ImplicitObjectLifetimeDefault` so
+    /// that surrounding code knows not to create a lifetime
+    /// parameter.
+    ImplicitObjectLifetimeDefault,
+
     /// Indicates an error during lowering (usually `'_` in wrong place)
     /// that was already reported.
     Error,
@@ -235,7 +248,9 @@ pub enum LifetimeName {
 impl LifetimeName {
     pub fn ident(&self) -> Ident {
         match *self {
-            LifetimeName::Implicit | LifetimeName::Error => Ident::invalid(),
+            LifetimeName::ImplicitObjectLifetimeDefault
+                | LifetimeName::Implicit
+                | LifetimeName::Error => Ident::invalid(),
             LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime),
             LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime),
             LifetimeName::Param(param_name) => param_name.ident(),
@@ -244,7 +259,9 @@ impl LifetimeName {
 
     pub fn is_elided(&self) -> bool {
         match self {
-            LifetimeName::Implicit | LifetimeName::Underscore => true,
+            LifetimeName::ImplicitObjectLifetimeDefault
+            | LifetimeName::Implicit
+            | LifetimeName::Underscore => true,
 
             // It might seem surprising that `Fresh(_)` counts as
             // *not* elided -- but this is because, as far as the code
diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 74031541f56..3b604bef78e 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -5,6 +5,8 @@
 //! used between functions, and they operate in a purely top-down
 //! way. Therefore, we break lifetime name resolution into a separate pass.
 
+// ignore-tidy-filelength
+
 use crate::hir::def::{Res, DefKind};
 use crate::hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
 use crate::hir::map::Map;
@@ -591,6 +593,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 }
                 match lifetime.name {
                     LifetimeName::Implicit => {
+                        // For types like `dyn Foo`, we should
+                        // generate a special form of elided.
+                        span_bug!(
+                            ty.span,
+                            "object-lifetime-default expected, not implict",
+                        );
+                    }
+                    LifetimeName::ImplicitObjectLifetimeDefault => {
                         // If the user does not write *anything*, we
                         // use the object lifetime defaulting
                         // rules. So e.g., `Box<dyn Debug>` becomes
@@ -2643,6 +2653,13 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                         hir::LifetimeName::Param(_) | hir::LifetimeName::Implicit => {
                             self.resolve_lifetime_ref(lt);
                         }
+                        hir::LifetimeName::ImplicitObjectLifetimeDefault => {
+                            self.tcx.sess.delay_span_bug(
+                                lt.span,
+                                "lowering generated `ImplicitObjectLifetimeDefault` \
+                                 outside of an object type",
+                            )
+                        }
                         hir::LifetimeName::Error => {
                             // No need to do anything, error already reported.
                         }
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
index 3f5b2f4bce7..ca68b9e31b6 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
@@ -578,7 +578,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 })
             }
 
-            hir::LifetimeName::Implicit => {
+            hir::LifetimeName::ImplicitObjectLifetimeDefault
+            | hir::LifetimeName::Implicit => {
                 // In this case, the user left off the lifetime; so
                 // they wrote something like:
                 //
diff --git a/src/test/ui/async-await/issues/issue-62517-1.rs b/src/test/ui/async-await/issues/issue-62517-1.rs
new file mode 100644
index 00000000000..5955d9751af
--- /dev/null
+++ b/src/test/ui/async-await/issues/issue-62517-1.rs
@@ -0,0 +1,23 @@
+// Regression test for #62517. We used to ICE when you had an `async
+// fn` with an `impl Trait` return that mentioned a `dyn Bar` with no
+// explicit lifetime bound.
+//
+// edition:2018
+// check-pass
+
+#![feature(async_await)]
+
+trait FirstTrait {}
+trait SecondTrait {
+    type Item: ?Sized;
+}
+
+async fn foo(x: &str) -> impl SecondTrait<Item = dyn FirstTrait> {
+}
+
+
+impl<T> SecondTrait for T {
+    type Item = dyn FirstTrait;
+}
+
+fn main() { }
diff --git a/src/test/ui/async-await/issues/issue-62517-2.rs b/src/test/ui/async-await/issues/issue-62517-2.rs
new file mode 100644
index 00000000000..72dae58e516
--- /dev/null
+++ b/src/test/ui/async-await/issues/issue-62517-2.rs
@@ -0,0 +1,16 @@
+// Regression test for #62517. We used to ICE when you had an `async
+// fn` with an `impl Trait` return that mentioned a `dyn Bar` with no
+// explicit lifetime bound.
+//
+// edition:2018
+
+#![feature(async_await)]
+
+trait Object {}
+
+trait Alpha<Param> {}
+
+async fn foo<'a>(_: &'a ()) -> impl Alpha<dyn Object> {}
+//~^ ERROR not satisfied
+
+fn main() { }
diff --git a/src/test/ui/async-await/issues/issue-62517-2.stderr b/src/test/ui/async-await/issues/issue-62517-2.stderr
new file mode 100644
index 00000000000..4f9b3047bfe
--- /dev/null
+++ b/src/test/ui/async-await/issues/issue-62517-2.stderr
@@ -0,0 +1,11 @@
+error[E0277]: the trait bound `(): Alpha<(dyn Object + 'static)>` is not satisfied
+  --> $DIR/issue-62517-2.rs:13:32
+   |
+LL | async fn foo<'a>(_: &'a ()) -> impl Alpha<dyn Object> {}
+   |                                ^^^^^^^^^^^^^^^^^^^^^^ the trait `Alpha<(dyn Object + 'static)>` is not implemented for `()`
+   |
+   = note: the return type of a function must have a statically known size
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/impl-trait/dyn-trait-elided-two-inputs-assoc.rs b/src/test/ui/impl-trait/dyn-trait-elided-two-inputs-assoc.rs
new file mode 100644
index 00000000000..3b714157384
--- /dev/null
+++ b/src/test/ui/impl-trait/dyn-trait-elided-two-inputs-assoc.rs
@@ -0,0 +1,16 @@
+// Test that we don't get an error with `dyn Bar` in an impl Trait
+// when there are multiple inputs.  The `dyn Bar` should default to `+
+// 'static`. This used to erroneously generate an error (cc #62517).
+//
+// check-pass
+
+trait Foo { type Item: ?Sized; }
+trait Bar { }
+
+impl<T> Foo for T {
+    type Item = dyn Bar;
+}
+
+fn foo(x: &str, y: &str) -> impl Foo<Item = dyn Bar> { () }
+
+fn main() { }
diff --git a/src/test/ui/impl-trait/dyn-trait-elided-two-inputs-param.rs b/src/test/ui/impl-trait/dyn-trait-elided-two-inputs-param.rs
new file mode 100644
index 00000000000..e8da52aad0e
--- /dev/null
+++ b/src/test/ui/impl-trait/dyn-trait-elided-two-inputs-param.rs
@@ -0,0 +1,11 @@
+// Test that we don't get an error with `dyn Object` in an impl Trait
+// when there are multiple inputs.  The `dyn Object` should default to `+
+// 'static`. This used to erroneously generate an error (cc #62517).
+//
+// check-pass
+
+trait Alpha<Item: ?Sized> {}
+trait Object {}
+impl<T> Alpha<dyn Object> for T {}
+fn alpha(x: &str, y: &str) -> impl Alpha<dyn Object> { () }
+fn main() { }
diff --git a/src/test/ui/impl-trait/dyn-trait-elided-two-inputs-ref-param.rs b/src/test/ui/impl-trait/dyn-trait-elided-two-inputs-ref-param.rs
new file mode 100644
index 00000000000..8d34c1b6c2a
--- /dev/null
+++ b/src/test/ui/impl-trait/dyn-trait-elided-two-inputs-ref-param.rs
@@ -0,0 +1,23 @@
+// Test that `impl Alpha<dyn Object>` resets the object-lifetime
+// default to `'static`.
+//
+// check-pass
+
+trait Alpha<Item: ?Sized> {
+    fn item(&self) -> Box<Item> {
+        panic!()
+    }
+}
+
+trait Object {}
+impl<T> Alpha<dyn Object> for T {}
+fn alpha(x: &str, y: &str) -> impl Alpha<dyn Object> { () }
+fn is_static<T>(_: T) where T: 'static { }
+
+fn bar(x: &str) -> &impl Alpha<dyn Object> { &() }
+
+fn main() {
+    let s = format!("foo");
+    let r = bar(&s);
+    is_static(r.item());
+}
diff --git a/src/test/ui/lifetimes/lifetime-elision-return-type-trait.rs b/src/test/ui/lifetimes/lifetime-elision-return-type-trait.rs
index 2370084b072..ea0d0ccbc55 100644
--- a/src/test/ui/lifetimes/lifetime-elision-return-type-trait.rs
+++ b/src/test/ui/lifetimes/lifetime-elision-return-type-trait.rs
@@ -6,7 +6,7 @@ trait Future {
 use std::error::Error;
 
 fn foo() -> impl Future<Item=(), Error=Box<dyn Error>> {
-//~^ ERROR missing lifetime
+//~^ ERROR not satisfied
     Ok(())
 }
 
diff --git a/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr b/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr
index 06b317ce952..228582d0001 100644
--- a/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr
+++ b/src/test/ui/lifetimes/lifetime-elision-return-type-trait.stderr
@@ -1,11 +1,11 @@
-error[E0106]: missing lifetime specifier
-  --> $DIR/lifetime-elision-return-type-trait.rs:8:44
+error[E0277]: the trait bound `std::result::Result<(), _>: Future` is not satisfied
+  --> $DIR/lifetime-elision-return-type-trait.rs:8:13
    |
 LL | fn foo() -> impl Future<Item=(), Error=Box<dyn Error>> {
-   |                                            ^^^^^^^^^ help: consider giving it a 'static lifetime: `dyn Error + 'static`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Future` is not implemented for `std::result::Result<(), _>`
    |
-   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+   = note: the return type of a function must have a statically known size
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0106`.
+For more information about this error, try `rustc --explain E0277`.