diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 972278bdf86..9f51eb8c35a 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -420,7 +420,10 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime LifetimeName::Name(name) => { visitor.visit_name(lifetime.span, name); } - LifetimeName::Static | LifetimeName::Implicit | LifetimeName::Underscore => {} + LifetimeName::Fresh(_) | + LifetimeName::Static | + LifetimeName::Implicit | + LifetimeName::Underscore => {} } } diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 36eafb61768..ad848949f62 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -107,6 +107,12 @@ pub struct LoweringContext<'a> { is_in_loop_condition: bool, is_in_trait_impl: bool, + /// What to do when we encounter either an "anonymous lifetime + /// reference". The term "anonymous" is meant to encompass both + /// `'_` lifetimes as well as fully elided cases where nothing is + /// written at all (e.g., `&T` or `std::cell::Ref`). + anonymous_lifetime_mode: AnonymousLifetimeMode, + // This is a list of in-band type definitions being generated by // Argument-position `impl Trait`. // When traversing a signature such as `fn foo(x: impl Trait)`, @@ -212,6 +218,7 @@ pub fn lower_crate( catch_scopes: Vec::new(), loop_scopes: Vec::new(), is_in_loop_condition: false, + anonymous_lifetime_mode: AnonymousLifetimeMode::PassThrough, type_def_lifetime_params: DefIdMap(), current_hir_id_owner: vec![(CRATE_DEF_INDEX, 0)], item_local_id_counters: NodeMap(), @@ -244,6 +251,51 @@ enum ParenthesizedGenericArgs { Err, } +/// What to do when we encounter an **anonymous** lifetime +/// reference. Anonymous lifetime references come in two flavors. You +/// have implicit, or fully elided, references to lifetimes, like the +/// one in `&T` or `Ref`, and you have `'_` lifetimes, like `&'_ T` +/// or `Ref<'_, T>`. These often behave the same, but not always: +/// +/// - certain usages of implicit references are deprecated, like +/// `Ref`, and we sometimes just give hard errors in those cases +/// as well. +/// - for object bounds there is a difference: `Box` is not +/// the same as `Box`. +/// +/// We describe the effects of the various modes in terms of three cases: +/// +/// - **Modern** -- includes all uses of `'_`, but also the lifetime arg +/// of a `&` (e.g., the missing lifetime in something like `&T`) +/// - **Dyn Bound** -- if you have something like `Box`, +/// there is an elided lifetime bound (`Box`). These +/// elided bounds follow special rules. Note that this only covers +/// cases where *nothing* is written; the `'_` in `Box` is a case of "modern" elision. +/// - **Deprecated** -- this coverse cases like `Ref`, where the lifetime +/// parameter to ref is completely elided. `Ref<'_, T>` would be the modern, +/// non-deprecated equivalent. +/// +/// Currently, the handling of lifetime elision is somewhat spread out +/// between HIR lowering and -- as described below -- the +/// `resolve_lifetime` module. Often we "fallthrough" to that code by generating +/// an "elided" or "underscore" lifetime name. In the future, we probably want to move +/// everything into HIR lowering. +#[derive(Copy, Clone)] +enum AnonymousLifetimeMode { + /// For **Modern** cases, create a new anonymous region parameter + /// and reference that. + /// + /// For **Dyn Bound** cases, pass responsibility to + /// `resolve_lifetime` code. + /// + /// For **Deprecated** cases, report an error. + CreateParameter, + + /// Pass responsibility to `resolve_lifetime` code for all cases. + PassThrough, +} + impl<'a> LoweringContext<'a> { fn lower_crate(mut self, c: &Crate) -> hir::Crate { /// Full-crate AST visitor that inserts into a fresh @@ -546,22 +598,37 @@ impl<'a> LoweringContext<'a> { span.with_ctxt(SyntaxContext::empty().apply_mark(mark)) } - // Creates a new hir::GenericParam for every new lifetime and type parameter - // encountered while evaluating `f`. Definitions are created with the parent - // provided. If no `parent_id` is provided, no definitions will be returned. - fn collect_in_band_defs(&mut self, parent_id: DefId, f: F) -> (Vec, T) + /// Creates a new hir::GenericParam for every new lifetime and + /// type parameter encountered while evaluating `f`. Definitions + /// are created with the parent provided. If no `parent_id` is + /// provided, no definitions will be returned. + /// + /// Presuming that in-band lifetimes are enabled, then + /// `self.anonymous_lifetime_mode` will be updated to match the + /// argument while `f` is running (and restored afterwards). + fn collect_in_band_defs( + &mut self, + parent_id: DefId, + anonymous_lifetime_mode: AnonymousLifetimeMode, + f: F, + ) -> (Vec, T) where F: FnOnce(&mut LoweringContext) -> T, { assert!(!self.is_collecting_in_band_lifetimes); assert!(self.lifetimes_to_define.is_empty()); + let old_anonymous_lifetime_mode = self.anonymous_lifetime_mode; + self.is_collecting_in_band_lifetimes = self.sess.features_untracked().in_band_lifetimes; + if self.is_collecting_in_band_lifetimes { + self.anonymous_lifetime_mode = anonymous_lifetime_mode; + } assert!(self.in_band_ty_params.is_empty()); - let res = f(self); self.is_collecting_in_band_lifetimes = false; + self.anonymous_lifetime_mode = old_anonymous_lifetime_mode; let in_band_ty_params = self.in_band_ty_params.split_off(0); let lifetimes_to_define = self.lifetimes_to_define.split_off(0); @@ -571,8 +638,12 @@ impl<'a> LoweringContext<'a> { .map(|(span, hir_name)| { let def_node_id = self.next_id().node_id; + // Get the name we'll use to make the def-path. Note + // that collisions are ok here and this shouldn't + // really show up for end-user. let str_name = match hir_name { hir::LifetimeName::Name(n) => n.as_str(), + hir::LifetimeName::Fresh(_) => keywords::UnderscoreLifetime.name().as_str(), hir::LifetimeName::Implicit | hir::LifetimeName::Underscore | hir::LifetimeName::Static => { @@ -636,6 +707,16 @@ impl<'a> LoweringContext<'a> { self.lifetimes_to_define.push((span, hir_name)); } + /// When we have either an elided or `'_` lifetime in an impl + /// header, we convert it to + fn collect_fresh_in_band_lifetime(&mut self, span: Span) -> hir::LifetimeName { + assert!(self.is_collecting_in_band_lifetimes); + let index = self.lifetimes_to_define.len(); + let hir_name = hir::LifetimeName::Fresh(index); + self.lifetimes_to_define.push((span, hir_name)); + hir_name + } + // Evaluates `f` with the lifetimes in `lt_defs` in-scope. // This is used to track which lifetimes have already been defined, and // which are new in-band lifetimes that need to have a definition created @@ -677,12 +758,17 @@ impl<'a> LoweringContext<'a> { res } - // Appends in-band lifetime defs and argument-position `impl Trait` defs - // to the existing set of generics. + /// Appends in-band lifetime defs and argument-position `impl + /// Trait` defs to the existing set of generics. + /// + /// Presuming that in-band lifetimes are enabled, then + /// `self.anonymous_lifetime_mode` will be updated to match the + /// argument while `f` is running (and restored afterwards). fn add_in_band_defs( &mut self, generics: &Generics, parent_id: DefId, + anonymous_lifetime_mode: AnonymousLifetimeMode, f: F, ) -> (hir::Generics, T) where @@ -694,7 +780,7 @@ impl<'a> LoweringContext<'a> { _ => None, }), |this| { - this.collect_in_band_defs(parent_id, |this| { + this.collect_in_band_defs(parent_id, anonymous_lifetime_mode, |this| { (this.lower_generics(generics), f(this)) }) }, @@ -1213,6 +1299,7 @@ impl<'a> LoweringContext<'a> { return; } } + name @ hir::LifetimeName::Fresh(_) => name, name @ hir::LifetimeName::Name(_) => name, hir::LifetimeName::Static => return, }; @@ -1757,19 +1844,35 @@ impl<'a> LoweringContext<'a> { } fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime { - let name = match self.lower_ident(l.ident) { - x if x == "'_" => hir::LifetimeName::Underscore, - x if x == "'static" => hir::LifetimeName::Static, + match self.lower_ident(l.ident) { + x if x == "'static" => self.new_named_lifetime(l.id, l.span, hir::LifetimeName::Static), + x if x == "'_" => match self.anonymous_lifetime_mode { + AnonymousLifetimeMode::CreateParameter => { + let fresh_name = self.collect_fresh_in_band_lifetime(l.span); + self.new_named_lifetime(l.id, l.span, fresh_name) + } + + AnonymousLifetimeMode::PassThrough => { + self.new_named_lifetime(l.id, l.span, hir::LifetimeName::Underscore) + } + }, name => { self.maybe_collect_in_band_lifetime(l.span, name); - hir::LifetimeName::Name(name) + self.new_named_lifetime(l.id, l.span, hir::LifetimeName::Name(name)) } - }; + } + } + fn new_named_lifetime( + &mut self, + id: NodeId, + span: Span, + name: hir::LifetimeName, + ) -> hir::Lifetime { hir::Lifetime { - id: self.lower_node_id(l.id).node_id, - name, - span: l.span, + id: self.lower_node_id(id).node_id, + span, + name: name, } } @@ -2115,9 +2218,12 @@ impl<'a> LoweringContext<'a> { let body = this.lower_block(body, false); this.expr_block(body, ThinVec::new()) }); - let (generics, fn_decl) = this.add_in_band_defs(generics, fn_def_id, |this| { - this.lower_fn_decl(decl, Some(fn_def_id), true) - }); + let (generics, fn_decl) = this.add_in_band_defs( + generics, + fn_def_id, + AnonymousLifetimeMode::PassThrough, + |this| this.lower_fn_decl(decl, Some(fn_def_id), true), + ); hir::ItemFn( fn_decl, @@ -2178,8 +2284,11 @@ impl<'a> LoweringContext<'a> { // method, it will not be considered an in-band // lifetime to be added, but rather a reference to a // parent lifetime. - let (generics, (trait_ref, lowered_ty)) = - self.add_in_band_defs(ast_generics, def_id, |this| { + let (generics, (trait_ref, lowered_ty)) = self.add_in_band_defs( + ast_generics, + def_id, + AnonymousLifetimeMode::CreateParameter, + |this| { let trait_ref = trait_ref.as_ref().map(|trait_ref| { this.lower_trait_ref(trait_ref, ImplTraitContext::Disallowed) }); @@ -2193,7 +2302,8 @@ impl<'a> LoweringContext<'a> { let lowered_ty = this.lower_ty(ty, ImplTraitContext::Disallowed); (trait_ref, lowered_ty) - }); + }, + ); let new_impl_items = self.with_in_scope_lifetime_defs( ast_generics.params.iter().filter_map(|p| match p { @@ -2378,12 +2488,17 @@ impl<'a> LoweringContext<'a> { ), TraitItemKind::Method(ref sig, None) => { let names = this.lower_fn_args_to_names(&sig.decl); - this.add_in_band_defs(&i.generics, trait_item_def_id, |this| { - hir::TraitItemKind::Method( - this.lower_method_sig(sig, trait_item_def_id, false), - hir::TraitMethod::Required(names), - ) - }) + this.add_in_band_defs( + &i.generics, + trait_item_def_id, + AnonymousLifetimeMode::PassThrough, + |this| { + hir::TraitItemKind::Method( + this.lower_method_sig(sig, trait_item_def_id, false), + hir::TraitMethod::Required(names), + ) + }, + ) } TraitItemKind::Method(ref sig, Some(ref body)) => { let body_id = this.lower_body(Some(&sig.decl), |this| { @@ -2391,12 +2506,17 @@ impl<'a> LoweringContext<'a> { this.expr_block(body, ThinVec::new()) }); - this.add_in_band_defs(&i.generics, trait_item_def_id, |this| { - hir::TraitItemKind::Method( - this.lower_method_sig(sig, trait_item_def_id, false), - hir::TraitMethod::Provided(body_id), - ) - }) + this.add_in_band_defs( + &i.generics, + trait_item_def_id, + AnonymousLifetimeMode::PassThrough, + |this| { + hir::TraitItemKind::Method( + this.lower_method_sig(sig, trait_item_def_id, false), + hir::TraitMethod::Provided(body_id), + ) + }, + ) } TraitItemKind::Type(ref bounds, ref default) => ( this.lower_generics(&i.generics), @@ -2470,12 +2590,21 @@ impl<'a> LoweringContext<'a> { }); let impl_trait_return_allow = !this.is_in_trait_impl; - this.add_in_band_defs(&i.generics, impl_item_def_id, |this| { - hir::ImplItemKind::Method( - this.lower_method_sig(sig, impl_item_def_id, impl_trait_return_allow), - body_id, - ) - }) + this.add_in_band_defs( + &i.generics, + impl_item_def_id, + AnonymousLifetimeMode::PassThrough, + |this| { + hir::ImplItemKind::Method( + this.lower_method_sig( + sig, + impl_item_def_id, + impl_trait_return_allow, + ), + body_id, + ) + }, + ) } ImplItemKind::Type(ref ty) => ( this.lower_generics(&i.generics), @@ -2598,14 +2727,18 @@ impl<'a> LoweringContext<'a> { attrs: this.lower_attrs(&i.attrs), node: match i.node { ForeignItemKind::Fn(ref fdec, ref generics) => { - let (generics, (fn_dec, fn_args)) = - this.add_in_band_defs(generics, def_id, |this| { + let (generics, (fn_dec, fn_args)) = this.add_in_band_defs( + generics, + def_id, + AnonymousLifetimeMode::PassThrough, + |this| { ( // Disallow impl Trait in foreign items this.lower_fn_decl(fdec, None, false), this.lower_fn_args_to_names(fdec), ) - }); + }, + ); hir::ForeignItemFn(fn_dec, fn_args, generics) } @@ -4017,7 +4150,21 @@ impl<'a> LoweringContext<'a> { /// Invoked to create the lifetime argument for a type `&T` /// with no explicit lifetime. fn elided_ref_lifetime(&mut self, span: Span) -> hir::Lifetime { - self.new_implicit_lifetime(span) + match self.anonymous_lifetime_mode { + // Intercept when we are in an impl header and introduce an in-band lifetime. + // Hence `impl Foo for &u32` becomes `impl<'f> Foo for &'f u32` for some fresh + // `'f`. + AnonymousLifetimeMode::CreateParameter => { + let fresh_name = self.collect_fresh_in_band_lifetime(span); + hir::Lifetime { + id: self.next_id().node_id, + span, + name: fresh_name, + } + } + + AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span), + } } /// Invoked to create the lifetime argument(s) for a path like @@ -4025,7 +4172,22 @@ impl<'a> LoweringContext<'a> { /// sorts of cases are deprecated. This may therefore report a warning or an /// error, depending on the mode. fn elided_path_lifetimes(&mut self, span: Span, count: usize) -> P<[hir::Lifetime]> { - (0..count).map(|_| self.new_implicit_lifetime(span)).collect() + match self.anonymous_lifetime_mode { + // NB. We intentionally ignore the create-parameter mode here + // and instead "pass through" to resolve-lifetimes, which will then + // report an error. This is because we don't want to support + // impl elision for deprecated forms like + // + // impl Foo for std::cell::Ref // note lack of '_ + AnonymousLifetimeMode::CreateParameter => {} + + // This is the normal case. + AnonymousLifetimeMode::PassThrough => {} + } + + (0..count) + .map(|_| self.new_implicit_lifetime(span)) + .collect() } /// Invoked to create the lifetime argument(s) for an elided trait object @@ -4033,6 +4195,26 @@ impl<'a> LoweringContext<'a> { /// when the bound is written, even if it is written with `'_` like in /// `Box`. In those cases, `lower_lifetime` is invoked. fn elided_dyn_bound(&mut self, span: Span) -> hir::Lifetime { + match self.anonymous_lifetime_mode { + // NB. We intentionally ignore the create-parameter mode here. + // and instead "pass through" to resolve-lifetimes, which will apply + // the object-lifetime-defaulting rules. Elided object lifetime defaults + // do not act like other elided lifetimes. In other words, given this: + // + // impl Foo for Box + // + // we do not introduce a fresh `'_` to serve as the bound, but instead + // ultimately translate to the equivalent of: + // + // impl Foo for Box + // + // `resolve_lifetime` has the code to make that happen. + AnonymousLifetimeMode::CreateParameter => {} + + // This is the normal case. + AnonymousLifetimeMode::PassThrough => {} + } + self.new_implicit_lifetime(span) } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index d94fdb82d99..c35b79937cd 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -209,6 +209,21 @@ pub enum LifetimeName { /// User typed `'_`. Underscore, + /// Synthetic name generated when user elided a lifetime in an impl header, + /// e.g. the lifetimes in cases like these: + /// + /// impl Foo for &u32 + /// impl Foo<'_> for u32 + /// + /// in that case, we rewrite to + /// + /// impl<'f> Foo for &'f u32 + /// impl<'f> Foo<'f> for u32 + /// + /// where `'f` is something like `Fresh(0)`. The indices are + /// unique per impl, but not necessarily continuous. + Fresh(usize), + /// User wrote `'static` Static, @@ -221,7 +236,7 @@ impl LifetimeName { use self::LifetimeName::*; match *self { Implicit => keywords::Invalid.name(), - Underscore => keywords::UnderscoreLifetime.name(), + Fresh(_) | Underscore => keywords::UnderscoreLifetime.name(), Static => keywords::StaticLifetime.name(), Name(name) => name, } @@ -242,7 +257,13 @@ impl Lifetime { use self::LifetimeName::*; match self.name { Implicit | Underscore => true, - Static | Name(_) => false, + + // It might seem surprising that `Fresh(_)` counts as + // *not* elided -- but this is because, as far as the code + // in the compiler is concerned -- `Fresh(_)` variants act + // equivalently to "some fresh name". They correspond to + // early-bound regions on an impl, in other words. + Fresh(_) | Static | Name(_) => false, } } diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index 774b1442b71..c3b3e10201f 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -145,6 +145,7 @@ impl<'a> HashStable> for hir::ImplItemId { impl_stable_hash_for!(enum hir::LifetimeName { Implicit, Underscore, + Fresh(index), Static, Name(name) }); diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 628cdce0dc7..ceda72dcd7a 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -582,7 +582,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // cc #48468 self.resolve_elided_lifetimes(slice::from_ref(lifetime), false) } - LifetimeName::Static | LifetimeName::Name(_) => { + LifetimeName::Fresh(_) | LifetimeName::Static | LifetimeName::Name(_) => { // If the user wrote an explicit name, use that. self.visit_lifetime(lifetime); } @@ -2086,7 +2086,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { ); err.emit(); } - hir::LifetimeName::Implicit | hir::LifetimeName::Name(_) => {} + hir::LifetimeName::Fresh(_) | hir::LifetimeName::Implicit | + hir::LifetimeName::Name(_) => {} } } @@ -2138,7 +2139,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { )) .emit(); } - hir::LifetimeName::Implicit | hir::LifetimeName::Name(_) => { + hir::LifetimeName::Fresh(_) | hir::LifetimeName::Implicit | + hir::LifetimeName::Name(_) => { self.resolve_lifetime_ref(bound); } } diff --git a/src/test/ui/feature-gate-in_band_lifetimes-impl.rs b/src/test/ui/feature-gate-in_band_lifetimes-impl.rs new file mode 100644 index 00000000000..a02b3e80009 --- /dev/null +++ b/src/test/ui/feature-gate-in_band_lifetimes-impl.rs @@ -0,0 +1,22 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(warnings)] +#![feature(underscore_lifetimes)] + +trait MyTrait<'a> { } + +impl<'a> MyTrait<'a> for &u32 { } +//~^ ERROR missing lifetime specifier + +impl<'a> MyTrait<'_> for &'a f32 { } +//~^ ERROR missing lifetime specifier + +fn main() {} diff --git a/src/test/ui/feature-gate-in_band_lifetimes-impl.stderr b/src/test/ui/feature-gate-in_band_lifetimes-impl.stderr new file mode 100644 index 00000000000..e32a06c3ce4 --- /dev/null +++ b/src/test/ui/feature-gate-in_band_lifetimes-impl.stderr @@ -0,0 +1,15 @@ +error[E0106]: missing lifetime specifier + --> $DIR/feature-gate-in_band_lifetimes-impl.rs:16:26 + | +LL | impl<'a> MyTrait<'a> for &u32 { } + | ^ expected lifetime parameter + +error[E0106]: missing lifetime specifier + --> $DIR/feature-gate-in_band_lifetimes-impl.rs:19:18 + | +LL | impl<'a> MyTrait<'_> for &'a f32 { } + | ^^ expected lifetime parameter + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/in-band-lifetimes/impl/assoc-type.rs b/src/test/ui/in-band-lifetimes/impl/assoc-type.rs new file mode 100644 index 00000000000..54f38b2e729 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/impl/assoc-type.rs @@ -0,0 +1,38 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that we do not yet support elision in associated types, even +// when there is just one name we could take from the impl header. + +#![allow(warnings)] + +#![feature(in_band_lifetimes)] +#![feature(underscore_lifetimes)] + +trait MyTrait { + type Output; +} + +impl MyTrait for &i32 { + type Output = &i32; + //~^ ERROR missing lifetime specifier +} + +impl MyTrait for &u32 { + type Output = &'_ i32; + //~^ ERROR missing lifetime specifier +} + +// This is what you have to do: +impl MyTrait for &'a f32 { + type Output = &'a f32; +} + +fn main() { } diff --git a/src/test/ui/in-band-lifetimes/impl/assoc-type.stderr b/src/test/ui/in-band-lifetimes/impl/assoc-type.stderr new file mode 100644 index 00000000000..909b86daef0 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/impl/assoc-type.stderr @@ -0,0 +1,15 @@ +error[E0106]: missing lifetime specifier + --> $DIR/assoc-type.rs:24:19 + | +LL | type Output = &i32; + | ^ expected lifetime parameter + +error[E0106]: missing lifetime specifier + --> $DIR/assoc-type.rs:29:20 + | +LL | type Output = &'_ i32; + | ^^ expected lifetime parameter + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/in-band-lifetimes/impl/dyn-trait.rs b/src/test/ui/in-band-lifetimes/impl/dyn-trait.rs new file mode 100644 index 00000000000..e839248b0e3 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/impl/dyn-trait.rs @@ -0,0 +1,45 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that `impl MyTrait<'_> for &i32` is equivalent to `impl<'a, +// 'b> MyTrait<'a> for &'b i32`. + +#![allow(warnings)] + +#![feature(dyn_trait)] +#![feature(in_band_lifetimes)] +#![feature(underscore_lifetimes)] + +use std::fmt::Debug; + +// Equivalent to `Box`: +trait StaticTrait { } +impl StaticTrait for Box { } + +// Equivalent to `Box`: +trait NotStaticTrait { } +impl NotStaticTrait for Box { } + +fn static_val(_: T) { +} + +fn with_dyn_debug_static<'a>(x: Box) { + static_val(x); //~ ERROR cannot infer +} + +fn not_static_val(_: T) { +} + +fn with_dyn_debug_not_static<'a>(x: Box) { + not_static_val(x); // OK +} + +fn main() { +} diff --git a/src/test/ui/in-band-lifetimes/impl/dyn-trait.stderr b/src/test/ui/in-band-lifetimes/impl/dyn-trait.stderr new file mode 100644 index 00000000000..0054ca3d1a5 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/impl/dyn-trait.stderr @@ -0,0 +1,22 @@ +error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements + --> $DIR/dyn-trait.rs:34:16 + | +LL | static_val(x); //~ ERROR cannot infer + | ^ + | +note: first, the lifetime cannot outlive the lifetime 'a as defined on the function body at 33:1... + --> $DIR/dyn-trait.rs:33:1 + | +LL | fn with_dyn_debug_static<'a>(x: Box) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: ...so that the expression is assignable: + expected std::boxed::Box + found std::boxed::Box + = note: but, the lifetime must be valid for the static lifetime... + = note: ...so that the types are compatible: + expected StaticTrait + found StaticTrait + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0495`. diff --git a/src/test/ui/in-band-lifetimes/impl/path-elided.rs b/src/test/ui/in-band-lifetimes/impl/path-elided.rs new file mode 100644 index 00000000000..fa1b4523889 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/impl/path-elided.rs @@ -0,0 +1,23 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![allow(warnings)] + +#![feature(in_band_lifetimes)] +#![feature(underscore_lifetimes)] + +trait MyTrait { } + +struct Foo<'a> { x: &'a u32 } + +impl MyTrait for Foo { + //~^ ERROR missing lifetime specifier +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/impl/path-elided.stderr b/src/test/ui/in-band-lifetimes/impl/path-elided.stderr new file mode 100644 index 00000000000..19e69c61a03 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/impl/path-elided.stderr @@ -0,0 +1,9 @@ +error[E0106]: missing lifetime specifier + --> $DIR/path-elided.rs:19:18 + | +LL | impl MyTrait for Foo { + | ^^^ expected lifetime parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/in-band-lifetimes/impl/path-underscore.rs b/src/test/ui/in-band-lifetimes/impl/path-underscore.rs new file mode 100644 index 00000000000..56f2d93d9e0 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/impl/path-underscore.rs @@ -0,0 +1,47 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that `impl MyTrait for Foo<'_>` works. + +// run-pass + +#![allow(warnings)] + +#![feature(in_band_lifetimes)] +#![feature(underscore_lifetimes)] + +trait MyTrait { } + +struct Foo<'a> { x: &'a u32 } + +impl MyTrait for Foo<'_> { +} + +fn impls_my_trait() { } + +fn impls_my_trait_val(_: T) { + impls_my_trait::(); +} + +fn random_where_clause() +where for<'a> Foo<'a>: MyTrait { } + +fn main() { + let x = 22; + let f = Foo { x: &x }; + + // This type is `Foo<'x>` for a local lifetime `'x`; so the impl + // must apply to any lifetime to apply to this. + impls_my_trait_val(f); + + impls_my_trait::>(); + + random_where_clause(); +} diff --git a/src/test/ui/in-band-lifetimes/impl/ref-underscore.rs b/src/test/ui/in-band-lifetimes/impl/ref-underscore.rs new file mode 100644 index 00000000000..1b1035abeba --- /dev/null +++ b/src/test/ui/in-band-lifetimes/impl/ref-underscore.rs @@ -0,0 +1,43 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that `impl MyTrait for &i32` works and is equivalent to any lifetime. + +// run-pass + +#![allow(warnings)] + +#![feature(in_band_lifetimes)] +#![feature(underscore_lifetimes)] + +trait MyTrait { } + +impl MyTrait for &i32 { +} + +fn impls_my_trait() { } + +fn impls_my_trait_val(_: T) { + impls_my_trait::(); +} + +fn random_where_clause() +where for<'a> &'a i32: MyTrait { } + +fn main() { + let x = 22; + let f = &x; + + impls_my_trait_val(f); + + impls_my_trait::<&'static i32>(); + + random_where_clause(); +} diff --git a/src/test/ui/in-band-lifetimes/impl/trait-elided.rs b/src/test/ui/in-band-lifetimes/impl/trait-elided.rs new file mode 100644 index 00000000000..7594d66e078 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/impl/trait-elided.rs @@ -0,0 +1,21 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![allow(warnings)] + +#![feature(in_band_lifetimes)] +#![feature(underscore_lifetimes)] + +trait MyTrait<'a> { } + +impl MyTrait for u32 { + //~^ ERROR missing lifetime specifier +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/impl/trait-elided.stderr b/src/test/ui/in-band-lifetimes/impl/trait-elided.stderr new file mode 100644 index 00000000000..bb301882868 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/impl/trait-elided.stderr @@ -0,0 +1,9 @@ +error[E0106]: missing lifetime specifier + --> $DIR/trait-elided.rs:17:6 + | +LL | impl MyTrait for u32 { + | ^^^^^^^ expected lifetime parameter + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/in-band-lifetimes/impl/trait-underscore.rs b/src/test/ui/in-band-lifetimes/impl/trait-underscore.rs new file mode 100644 index 00000000000..077e33c1efd --- /dev/null +++ b/src/test/ui/in-band-lifetimes/impl/trait-underscore.rs @@ -0,0 +1,48 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that `impl MyTrait<'_> for &i32` is equivalent to `impl<'a, +// 'b> MyTrait<'a> for &'b i32`. +// +// run-pass + +#![allow(warnings)] + +#![feature(in_band_lifetimes)] +#![feature(underscore_lifetimes)] + +trait MyTrait<'a> { } + +// This is equivalent to `MyTrait<'a> for &'b i32`, which is proven by +// the code below. +impl MyTrait<'_> for &i32 { +} + +// When called, T will be `&'x i32` for some `'x`, so since we can +// prove that `&'x i32: for<'a> MyTrait<'a>, then we know that the +// lifetime parameter above is disconnected. +fn impls_my_trait MyTrait<'a>>() { } + +fn impls_my_trait_val MyTrait<'a>>(_: T) { + impls_my_trait::(); +} + +fn random_where_clause() +where for<'a, 'b> &'a i32: MyTrait<'b> { } + +fn main() { + let x = 22; + let f = &x; + impls_my_trait_val(f); + + impls_my_trait::<&'static i32>(); + + random_where_clause(); +}