diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index cba1a66c17c42e231d12dc447c180ac03195eeaa..8089a88a9e8d483a3b2ca0a1ca612a6282f45b25 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -2051,4 +2051,6 @@ fn main() { E0631, // type mismatch in closure arguments E0637, // "'_" is not a valid lifetime bound E0657, // `impl Trait` can only capture lifetimes bound at the fn level + E0687, // in-band lifetimes cannot be used in `fn`/`Fn` syntax + E0688, // in-band lifetimes cannot be mixed with explicit lifetime binders } diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 3e527f43fec20d9673ec57dc4e33cc146d7bf883..7fd6f4a8b4278254759cb7d07c41dac62ba8d4e5 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -106,6 +106,26 @@ pub struct LoweringContext<'a> { is_in_loop_condition: bool, is_in_trait_impl: bool, + // Used to create lifetime definitions from in-band lifetime usages. + // e.g. `fn foo(x: &'x u8) -> &'x u8` to `fn foo<'x>(x: &'x u8) -> &'x u8` + // When a named lifetime is encountered in a function or impl header and + // has not been defined + // (i.e. it doesn't appear in the in_scope_lifetimes list), it is added + // to this list. The results of this list are then added to the list of + // lifetime definitions in the corresponding impl or function generics. + lifetimes_to_define: Vec<(Span, Name)>, + // Whether or not in-band lifetimes are being collected. This is used to + // indicate whether or not we're in a place where new lifetimes will result + // in in-band lifetime definitions, such a function or an impl header. + // This will always be false unless the `in_band_lifetimes` feature is + // enabled. + is_collecting_in_band_lifetimes: bool, + // Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB. + // When `is_collectin_in_band_lifetimes` is true, each lifetime is checked + // against this list to see if it is already in-scope, or if a definition + // needs to be created for it. + in_scope_lifetimes: Vec, + type_def_lifetime_params: DefIdMap, current_hir_id_owner: Vec<(DefIndex, u32)>, @@ -177,6 +197,9 @@ pub fn lower_crate(sess: &Session, node_id_to_hir_id: IndexVec::new(), is_generator: false, is_in_trait_impl: false, + lifetimes_to_define: Vec::new(), + is_collecting_in_band_lifetimes: false, + in_scope_lifetimes: Vec::new(), }.lower_crate(krate) } @@ -271,13 +294,23 @@ fn visit_item(&mut self, item: &'lcx Item) { }); if item_lowered { - if let ItemKind::Impl(_,_,_,_,ref opt_trait_ref,_,_) = item.node { - self.with_trait_impl_ref(opt_trait_ref, |this| { - visit::walk_item(this, item) - }); - } else { - visit::walk_item(self, item); - } + let item_lifetimes = match self.lctx.items.get(&item.id).unwrap().node { + hir::Item_::ItemImpl(_,_,_,ref generics,..) | + hir::Item_::ItemTrait(_,_,ref generics,..) => + generics.lifetimes.clone(), + _ => Vec::new().into(), + }; + + self.lctx.with_parent_impl_lifetime_defs(&item_lifetimes, |this| { + let this = &mut ItemLowerer { lctx: this }; + if let ItemKind::Impl(_,_,_,_,ref opt_trait_ref,_,_) = item.node { + this.with_trait_impl_ref(opt_trait_ref, |this| { + visit::walk_item(this, item) + }); + } else { + visit::walk_item(this, item); + } + }); } } @@ -490,6 +523,124 @@ fn allow_internal_unstable(&self, reason: CompilerDesugaringKind, span: Span) -> span.with_ctxt(SyntaxContext::empty().apply_mark(mark)) } + // Creates a new hir::LifetimeDef for every new lifetime 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_lifetime_defs( + &mut self, + parent_id: Option, + f: F + ) -> (Vec, T) where F: FnOnce(&mut LoweringContext) -> T + { + assert!(!self.is_collecting_in_band_lifetimes); + assert!(self.lifetimes_to_define.is_empty()); + self.is_collecting_in_band_lifetimes = self.sess.features.borrow().in_band_lifetimes; + + let res = f(self); + + self.is_collecting_in_band_lifetimes = false; + + let lifetimes_to_define = self.lifetimes_to_define.split_off(0); + + let lifetime_defs = match parent_id { + Some(parent_id) => lifetimes_to_define.into_iter().map(|(span, name)| { + let def_node_id = self.next_id().node_id; + + // Add a definition for the in-band lifetime def + self.resolver.definitions().create_def_with_parent( + parent_id.index, + def_node_id, + DefPathData::LifetimeDef(name.as_str()), + DefIndexAddressSpace::High, + Mark::root() + ); + + hir::LifetimeDef { + lifetime: hir::Lifetime { + id: def_node_id, + span, + name: hir::LifetimeName::Name(name), + }, + bounds: Vec::new().into(), + pure_wrt_drop: false, + in_band: true, + } + }).collect(), + None => Vec::new(), + }; + + (lifetime_defs, res) + } + + // 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 + // for them. + fn with_in_scope_lifetime_defs( + &mut self, + lt_defs: &[LifetimeDef], + f: F + ) -> T where F: FnOnce(&mut LoweringContext) -> T + { + let old_len = self.in_scope_lifetimes.len(); + let lt_def_names = lt_defs.iter().map(|lt_def| lt_def.lifetime.ident.name); + self.in_scope_lifetimes.extend(lt_def_names); + + let res = f(self); + + self.in_scope_lifetimes.truncate(old_len); + res + } + + // Same as the method above, but accepts `hir::LifetimeDef`s + // instead of `ast::LifetimeDef`s. + // This should only be used with generics that have already had their + // in-band lifetimes added. In practice, this means that this function is + // only used when lowering a child item of a trait or impl. + fn with_parent_impl_lifetime_defs( + &mut self, + lt_defs: &[hir::LifetimeDef], + f: F + ) -> T where F: FnOnce(&mut LoweringContext) -> T + { + let old_len = self.in_scope_lifetimes.len(); + let lt_def_names = lt_defs.iter().map(|lt_def| lt_def.lifetime.name.name()); + self.in_scope_lifetimes.extend(lt_def_names); + + let res = f(self); + + self.in_scope_lifetimes.truncate(old_len); + res + } + + // Appends in-band lifetime defs to the existing set of out-of-band lifetime defs. + // Evaluates all within the context of the out-of-band defs. + // If provided, `impl_item_id` is used to find the parent impls of impl items so + // that their generics are not duplicated. + fn add_in_band_lifetime_defs( + &mut self, + generics: &Generics, + parent_id: Option, + f: F + ) -> (hir::Generics, T) + where F: FnOnce(&mut LoweringContext) -> T + { + let (in_band_defs, (mut lowered_generics, res)) = + self.with_in_scope_lifetime_defs(&generics.lifetimes, |this| { + this.collect_in_band_lifetime_defs(parent_id, |this| { + (this.lower_generics(generics), f(this)) + }) + }); + + lowered_generics.lifetimes = + lowered_generics.lifetimes + .iter().cloned() + .chain(in_band_defs.into_iter()) + .collect(); + + (lowered_generics, res) + } + fn with_catch_scope(&mut self, catch_id: NodeId, f: F) -> T where F: FnOnce(&mut LoweringContext) -> T { @@ -710,13 +861,14 @@ fn lower_ty(&mut self, t: &Ty, itctx: ImplTraitContext) -> P { hir::TyRptr(lifetime, self.lower_mt(mt, itctx)) } TyKind::BareFn(ref f) => { - hir::TyBareFn(P(hir::BareFnTy { - lifetimes: self.lower_lifetime_defs(&f.lifetimes), - unsafety: self.lower_unsafety(f.unsafety), - abi: f.abi, - decl: self.lower_fn_decl(&f.decl, None, false), - arg_names: self.lower_fn_args_to_names(&f.decl), - })) + self.with_in_scope_lifetime_defs(&f.lifetimes, |this| + hir::TyBareFn(P(hir::BareFnTy { + lifetimes: this.lower_lifetime_defs(&f.lifetimes), + unsafety: this.lower_unsafety(f.unsafety), + abi: f.abi, + decl: this.lower_fn_decl(&f.decl, None, false), + arg_names: this.lower_fn_args_to_names(&f.decl), + }))) } TyKind::Never => hir::TyNever, TyKind::Tup(ref tys) => { @@ -906,6 +1058,7 @@ fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) { lifetime: def_lifetime, bounds: Vec::new().into(), pure_wrt_drop: false, + in_band: false, }); } } @@ -1344,23 +1497,44 @@ fn lower_ty_params(&mut self, tps: &Vec, add_bounds: &NodeMap hir::Lifetime { + let name = match self.lower_ident(l.ident) { + x if x == "'_" => hir::LifetimeName::Underscore, + x if x == "'static" => hir::LifetimeName::Static, + name => { + if self.is_collecting_in_band_lifetimes && + !self.in_scope_lifetimes.contains(&name) && + self.lifetimes_to_define.iter() + .find(|&&(_, lt_name)| lt_name == name) + .is_none() + { + self.lifetimes_to_define.push((l.span, name)); + } + + hir::LifetimeName::Name(name) + } + }; + hir::Lifetime { id: self.lower_node_id(l.id).node_id, - name: match self.lower_ident(l.ident) { - x if x == "'_" => hir::LifetimeName::Underscore, - x if x == "'static" => hir::LifetimeName::Static, - name => hir::LifetimeName::Name(name), - }, + name, span: l.span, } } fn lower_lifetime_def(&mut self, l: &LifetimeDef) -> hir::LifetimeDef { - hir::LifetimeDef { + let was_collecting_in_band = self.is_collecting_in_band_lifetimes; + self.is_collecting_in_band_lifetimes = false; + + let def = hir::LifetimeDef { lifetime: self.lower_lifetime(&l.lifetime), bounds: self.lower_lifetimes(&l.bounds), pure_wrt_drop: l.attrs.iter().any(|attr| attr.check_name("may_dangle")), - } + in_band: false, + }; + + self.is_collecting_in_band_lifetimes = was_collecting_in_band; + + def } fn lower_lifetimes(&mut self, lts: &Vec) -> hir::HirVec { @@ -1435,15 +1609,19 @@ fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicat ref bounded_ty, ref bounds, span}) => { - hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - bound_lifetimes: self.lower_lifetime_defs(bound_lifetimes), - bounded_ty: self.lower_ty(bounded_ty, ImplTraitContext::Disallowed), - bounds: bounds.iter().filter_map(|bound| match *bound { - // Ignore `?Trait` bounds, they were copied into type parameters already. - TraitTyParamBound(_, TraitBoundModifier::Maybe) => None, - _ => Some(self.lower_ty_param_bound(bound, ImplTraitContext::Disallowed)) - }).collect(), - span, + self.with_in_scope_lifetime_defs(bound_lifetimes, |this| { + hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + bound_lifetimes: this.lower_lifetime_defs(bound_lifetimes), + bounded_ty: this.lower_ty(bounded_ty, ImplTraitContext::Disallowed), + bounds: bounds.iter().filter_map(|bound| match *bound { + // Ignore `?Trait` bounds. + // Tthey were copied into type parameters already. + TraitTyParamBound(_, TraitBoundModifier::Maybe) => None, + _ => Some(this.lower_ty_param_bound( + bound, ImplTraitContext::Disallowed)) + }).collect(), + span, + }) }) } WherePredicate::RegionPredicate(WhereRegionPredicate{ ref lifetime, @@ -1504,9 +1682,13 @@ fn lower_poly_trait_ref(&mut self, p: &PolyTraitRef, itctx: ImplTraitContext) -> hir::PolyTraitRef { + let bound_lifetimes = self.lower_lifetime_defs(&p.bound_lifetimes); + let trait_ref = self.with_parent_impl_lifetime_defs(&bound_lifetimes, + |this| this.lower_trait_ref(&p.trait_ref, itctx)); + hir::PolyTraitRef { - bound_lifetimes: self.lower_lifetime_defs(&p.bound_lifetimes), - trait_ref: self.lower_trait_ref(&p.trait_ref, itctx), + bound_lifetimes, + trait_ref, span: p.span, } } @@ -1678,11 +1860,15 @@ fn lower_item_kind(&mut self, let body = this.lower_block(body, false); this.expr_block(body, ThinVec::new()) }); - hir::ItemFn(this.lower_fn_decl(decl, fn_def_id, true), + let (generics, fn_decl) = + this.add_in_band_lifetime_defs(generics, fn_def_id, |this| + this.lower_fn_decl(decl, fn_def_id, true)); + + hir::ItemFn(fn_decl, this.lower_unsafety(unsafety), this.lower_constness(constness), abi, - this.lower_generics(generics), + generics, body_id) }) } @@ -1723,29 +1909,42 @@ fn lower_item_kind(&mut self, ItemKind::Impl(unsafety, polarity, defaultness, - ref generics, + ref ast_generics, ref ifce, ref ty, ref impl_items) => { - let new_impl_items = impl_items.iter() - .map(|item| self.lower_impl_item_ref(item)) - .collect(); - let ifce = ifce.as_ref().map(|trait_ref| { - self.lower_trait_ref(trait_ref, ImplTraitContext::Disallowed) + let def_id = self.resolver.definitions().opt_local_def_id(id); + let (generics, (ifce, lowered_ty)) = + self.add_in_band_lifetime_defs(ast_generics, def_id, |this| { + let ifce = ifce.as_ref().map(|trait_ref| { + this.lower_trait_ref(trait_ref, ImplTraitContext::Disallowed) + }); + + if let Some(ref trait_ref) = ifce { + if let Def::Trait(def_id) = trait_ref.path.def { + this.trait_impls.entry(def_id).or_insert(vec![]).push(id); + } + } + + let lowered_ty = this.lower_ty(ty, ImplTraitContext::Disallowed); + + (ifce, lowered_ty) + }); + + let new_impl_items = self.with_in_scope_lifetime_defs( + &ast_generics.lifetimes, |this| { + impl_items.iter() + .map(|item| this.lower_impl_item_ref(item)) + .collect() }); - if let Some(ref trait_ref) = ifce { - if let Def::Trait(def_id) = trait_ref.path.def { - self.trait_impls.entry(def_id).or_insert(vec![]).push(id); - } - } hir::ItemImpl(self.lower_unsafety(unsafety), self.lower_impl_polarity(polarity), self.lower_defaultness(defaultness, true /* [1] */), - self.lower_generics(generics), + generics, ifce, - self.lower_ty(ty, ImplTraitContext::Disallowed), + lowered_ty, new_impl_items) } ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref items) => { @@ -1769,41 +1968,55 @@ fn lower_trait_item(&mut self, i: &TraitItem) -> hir::TraitItem { let LoweredNodeId { node_id, hir_id } = this.lower_node_id(i.id); let fn_def_id = this.resolver.definitions().opt_local_def_id(node_id); + let (generics, node) = match i.node { + TraitItemKind::Const(ref ty, ref default) => { + ( + this.lower_generics(&i.generics), + hir::TraitItemKind::Const( + this.lower_ty(ty, ImplTraitContext::Disallowed), + default.as_ref().map(|x| { + this.lower_body(None, |this| this.lower_expr(x)) + })) + ) + } + TraitItemKind::Method(ref sig, None) => { + let names = this.lower_fn_args_to_names(&sig.decl); + this.add_in_band_lifetime_defs(&i.generics, fn_def_id, |this| + hir::TraitItemKind::Method( + this.lower_method_sig(sig, fn_def_id, false), + hir::TraitMethod::Required(names))) + } + TraitItemKind::Method(ref sig, Some(ref body)) => { + let body_id = this.lower_body(Some(&sig.decl), |this| { + let body = this.lower_block(body, false); + this.expr_block(body, ThinVec::new()) + }); + + this.add_in_band_lifetime_defs(&i.generics, fn_def_id, |this| + hir::TraitItemKind::Method( + this.lower_method_sig(sig, fn_def_id, false), + hir::TraitMethod::Provided(body_id))) + } + TraitItemKind::Type(ref bounds, ref default) => { + ( + this.lower_generics(&i.generics), + hir::TraitItemKind::Type( + this.lower_bounds(bounds, ImplTraitContext::Disallowed), + default.as_ref().map(|x| { + this.lower_ty(x, ImplTraitContext::Disallowed) + })) + ) + } + TraitItemKind::Macro(..) => panic!("Shouldn't exist any more"), + }; + hir::TraitItem { id: node_id, hir_id, name: this.lower_ident(i.ident), attrs: this.lower_attrs(&i.attrs), - generics: this.lower_generics(&i.generics), - node: match i.node { - TraitItemKind::Const(ref ty, ref default) => { - hir::TraitItemKind::Const(this.lower_ty(ty, ImplTraitContext::Disallowed), - default.as_ref().map(|x| { - this.lower_body(None, |this| this.lower_expr(x)) - })) - } - TraitItemKind::Method(ref sig, None) => { - let names = this.lower_fn_args_to_names(&sig.decl); - hir::TraitItemKind::Method(this.lower_method_sig(sig, fn_def_id, false), - hir::TraitMethod::Required(names)) - } - TraitItemKind::Method(ref sig, Some(ref body)) => { - let body_id = this.lower_body(Some(&sig.decl), |this| { - let body = this.lower_block(body, false); - this.expr_block(body, ThinVec::new()) - }); - hir::TraitItemKind::Method(this.lower_method_sig(sig, fn_def_id, false), - hir::TraitMethod::Provided(body_id)) - } - TraitItemKind::Type(ref bounds, ref default) => { - hir::TraitItemKind::Type(this.lower_bounds(bounds, - ImplTraitContext::Disallowed), - default.as_ref().map(|x| { - this.lower_ty(x, ImplTraitContext::Disallowed) - })) - } - TraitItemKind::Macro(..) => panic!("Shouldn't exist any more"), - }, + generics, + node, span: i.span, } }) @@ -1838,37 +2051,46 @@ fn lower_impl_item(&mut self, i: &ImplItem) -> hir::ImplItem { let LoweredNodeId { node_id, hir_id } = this.lower_node_id(i.id); let fn_def_id = this.resolver.definitions().opt_local_def_id(node_id); + let (generics, node) = match i.node { + ImplItemKind::Const(ref ty, ref expr) => { + let body_id = this.lower_body(None, |this| this.lower_expr(expr)); + ( + this.lower_generics(&i.generics), + hir::ImplItemKind::Const( + this.lower_ty(ty, ImplTraitContext::Disallowed), + body_id + ) + ) + } + ImplItemKind::Method(ref sig, ref body) => { + let body_id = this.lower_body(Some(&sig.decl), |this| { + let body = this.lower_block(body, false); + this.expr_block(body, ThinVec::new()) + }); + let impl_trait_return_allow = !this.is_in_trait_impl; + + this.add_in_band_lifetime_defs(&i.generics, fn_def_id, |this| + hir::ImplItemKind::Method( + this.lower_method_sig(sig, fn_def_id, impl_trait_return_allow), + body_id)) + } + ImplItemKind::Type(ref ty) => ( + this.lower_generics(&i.generics), + hir::ImplItemKind::Type( + this.lower_ty(ty, ImplTraitContext::Disallowed)), + ), + ImplItemKind::Macro(..) => panic!("Shouldn't exist any more"), + }; + hir::ImplItem { id: node_id, hir_id, name: this.lower_ident(i.ident), attrs: this.lower_attrs(&i.attrs), - generics: this.lower_generics(&i.generics), + generics, vis: this.lower_visibility(&i.vis, None), defaultness: this.lower_defaultness(i.defaultness, true /* [1] */), - node: match i.node { - ImplItemKind::Const(ref ty, ref expr) => { - let body_id = this.lower_body(None, |this| this.lower_expr(expr)); - hir::ImplItemKind::Const( - this.lower_ty(ty, ImplTraitContext::Disallowed), - body_id - ) - } - ImplItemKind::Method(ref sig, ref body) => { - let body_id = this.lower_body(Some(&sig.decl), |this| { - let body = this.lower_block(body, false); - this.expr_block(body, ThinVec::new()) - }); - let impl_trait_return_allow = !this.is_in_trait_impl; - hir::ImplItemKind::Method(this.lower_method_sig(sig, - fn_def_id, - impl_trait_return_allow), - body_id) - } - ImplItemKind::Type(ref ty) => - hir::ImplItemKind::Type(this.lower_ty(ty, ImplTraitContext::Disallowed)), - ImplItemKind::Macro(..) => panic!("Shouldn't exist any more"), - }, + node, span: i.span, } }) @@ -1958,16 +2180,26 @@ pub fn lower_item(&mut self, i: &Item) -> Option { fn lower_foreign_item(&mut self, i: &ForeignItem) -> hir::ForeignItem { self.with_parent_def(i.id, |this| { + let node_id = this.lower_node_id(i.id).node_id; + let def_id = this.resolver.definitions().local_def_id(node_id); hir::ForeignItem { - id: this.lower_node_id(i.id).node_id, + id: node_id, name: i.ident.name, attrs: this.lower_attrs(&i.attrs), node: match i.node { ForeignItemKind::Fn(ref fdec, ref generics) => { // Disallow impl Trait in foreign items - hir::ForeignItemFn(this.lower_fn_decl(fdec, None, false), - this.lower_fn_args_to_names(fdec), - this.lower_generics(generics)) + let (generics, (fn_dec, fn_args)) = + this.add_in_band_lifetime_defs( + generics, + Some(def_id), + |this| ( + this.lower_fn_decl(fdec, None, false), + this.lower_fn_args_to_names(fdec) + ) + ); + + hir::ForeignItemFn(fn_dec, fn_args, generics) } ForeignItemKind::Static(ref t, m) => { hir::ForeignItemStatic(this.lower_ty(t, ImplTraitContext::Disallowed), m) diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 9ef82d09380de997ae818ae28308448ccd27ade9..39ec33eef1fec518542f3001a5335fd914459042 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -222,6 +222,10 @@ pub struct LifetimeDef { pub lifetime: Lifetime, pub bounds: HirVec, pub pure_wrt_drop: bool, + // Indicates that the lifetime definition was synthetically added + // as a result of an in-band lifetime usage like + // `fn foo(x: &'a u8) -> &'a u8 { x }` + pub in_band: bool, } /// A "Path" is essentially Rust's notion of a name; for instance: diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index 87ee28a7258c565c96aa4ae69d1fd5b45ae74e22..bccef6dc91bcdfa553f19f6f85b21cf1990a796f 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -157,7 +157,8 @@ fn hash_stable(&self, impl_stable_hash_for!(struct hir::LifetimeDef { lifetime, bounds, - pure_wrt_drop + pure_wrt_drop, + in_band }); impl_stable_hash_for!(struct hir::Path { diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 8f2ad98f85885bb1fb7db4c3a1eb2e59044ff95a..45e80b58a4f0d99a04707a787e0d0239480c06ad 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -493,10 +493,15 @@ fn hash_stable(&self, } } +impl_stable_hash_for!(enum ::middle::resolve_lifetime::LifetimeDefOrigin { + Explicit, + InBand +}); + impl_stable_hash_for!(enum ::middle::resolve_lifetime::Region { Static, - EarlyBound(index, decl), - LateBound(db_index, decl), + EarlyBound(index, decl, is_in_band), + LateBound(db_index, decl, is_in_band), LateBoundAnon(db_index, anon_index), Free(call_site_scope_data, decl) }); diff --git a/src/librustc/infer/error_reporting/different_lifetimes.rs b/src/librustc/infer/error_reporting/different_lifetimes.rs index c64bd610962eb440a4b4180cd2931c0fb227bcc4..cade67a44bb0e40adfff9f003db3ae3bdd264d8e 100644 --- a/src/librustc/infer/error_reporting/different_lifetimes.rs +++ b/src/librustc/infer/error_reporting/different_lifetimes.rs @@ -281,7 +281,7 @@ fn visit_ty(&mut self, arg: &'gcx hir::Ty) { // Find the index of the named region that was part of the // error. We will then search the function parameters for a bound // region at the right depth with the same index - (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { + (Some(rl::Region::EarlyBound(_, id, _)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound self.infcx.tcx.hir.local_def_id(id)={:?} \ def_id={:?}", id, def_id); if id == def_id { @@ -293,7 +293,10 @@ fn visit_ty(&mut self, arg: &'gcx hir::Ty) { // Find the index of the named region that was part of the // error. We will then search the function parameters for a bound // region at the right depth with the same index - (Some(rl::Region::LateBound(debruijn_index, id)), ty::BrNamed(def_id, _)) => { + ( + Some(rl::Region::LateBound(debruijn_index, id, _)), + ty::BrNamed(def_id, _) + ) => { debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index.depth); debug!("self.infcx.tcx.hir.local_def_id(id)={:?}", id); @@ -306,8 +309,8 @@ fn visit_ty(&mut self, arg: &'gcx hir::Ty) { (Some(rl::Region::Static), _) | (Some(rl::Region::Free(_, _)), _) | - (Some(rl::Region::EarlyBound(_, _)), _) | - (Some(rl::Region::LateBound(_, _)), _) | + (Some(rl::Region::EarlyBound(_, _, _)), _) | + (Some(rl::Region::LateBound(_, _, _)), _) | (Some(rl::Region::LateBoundAnon(_, _)), _) | (None, _) => { debug!("no arg found"); @@ -368,7 +371,7 @@ fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { } } - (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { + (Some(rl::Region::EarlyBound(_, id, _)), ty::BrNamed(def_id, _)) => { debug!("EarlyBound self.infcx.tcx.hir.local_def_id(id)={:?} \ def_id={:?}", id, def_id); if id == def_id { @@ -377,7 +380,7 @@ fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { } } - (Some(rl::Region::LateBound(debruijn_index, id)), ty::BrNamed(def_id, _)) => { + (Some(rl::Region::LateBound(debruijn_index, id, _)), ty::BrNamed(def_id, _)) => { debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index.depth); debug!("id={:?}", id); @@ -389,8 +392,8 @@ fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { } (Some(rl::Region::Static), _) | - (Some(rl::Region::EarlyBound(_, _)), _) | - (Some(rl::Region::LateBound(_, _)), _) | + (Some(rl::Region::EarlyBound(_, _, _)), _) | + (Some(rl::Region::LateBound(_, _, _)), _) | (Some(rl::Region::LateBoundAnon(_, _)), _) | (Some(rl::Region::Free(_, _)), _) | (None, _) => { diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 7ed646bb9dd756def3c835f087f2a67d20ccbbf0..b39975d3ff9198815f708881159b37462251b21a 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -36,11 +36,32 @@ use hir; use hir::intravisit::{self, Visitor, NestedVisitorMap}; +/// The origin of a named lifetime definition. +/// +/// This is used to prevent the usage of in-band lifetimes in `Fn`/`fn` syntax. +#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)] +pub enum LifetimeDefOrigin { + // Explicit binders like `fn foo<'a>(x: &'a u8)` + Explicit, + // In-band declarations like `fn foo(x: &'a u8)` + InBand, +} + +impl LifetimeDefOrigin { + fn from_is_in_band(is_in_band: bool) -> Self { + if is_in_band { + LifetimeDefOrigin::InBand + } else { + LifetimeDefOrigin::Explicit + } + } +} + #[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)] pub enum Region { Static, - EarlyBound(/* index */ u32, /* lifetime decl */ DefId), - LateBound(ty::DebruijnIndex, /* lifetime decl */ DefId), + EarlyBound(/* index */ u32, /* lifetime decl */ DefId, LifetimeDefOrigin), + LateBound(ty::DebruijnIndex, /* lifetime decl */ DefId, LifetimeDefOrigin), LateBoundAnon(ty::DebruijnIndex, /* anon index */ u32), Free(DefId, /* lifetime decl */ DefId), } @@ -52,14 +73,16 @@ fn early(hir_map: &Map, index: &mut u32, def: &hir::LifetimeDef) let i = *index; *index += 1; let def_id = hir_map.local_def_id(def.lifetime.id); + let origin = LifetimeDefOrigin::from_is_in_band(def.in_band); debug!("Region::early: index={} def_id={:?}", i, def_id); - (def.lifetime.name, Region::EarlyBound(i, def_id)) + (def.lifetime.name, Region::EarlyBound(i, def_id, origin)) } fn late(hir_map: &Map, def: &hir::LifetimeDef) -> (hir::LifetimeName, Region) { let depth = ty::DebruijnIndex::new(1); let def_id = hir_map.local_def_id(def.lifetime.id); - (def.lifetime.name, Region::LateBound(depth, def_id)) + let origin = LifetimeDefOrigin::from_is_in_band(def.in_band); + (def.lifetime.name, Region::LateBound(depth, def_id, origin)) } fn late_anon(index: &Cell) -> Region { @@ -74,16 +97,16 @@ fn id(&self) -> Option { Region::Static | Region::LateBoundAnon(..) => None, - Region::EarlyBound(_, id) | - Region::LateBound(_, id) | + Region::EarlyBound(_, id, _) | + Region::LateBound(_, id, _) | Region::Free(_, id) => Some(id) } } fn shifted(self, amount: u32) -> Region { match self { - Region::LateBound(depth, id) => { - Region::LateBound(depth.shifted(amount), id) + Region::LateBound(depth, id, origin) => { + Region::LateBound(depth.shifted(amount), id, origin) } Region::LateBoundAnon(depth, index) => { Region::LateBoundAnon(depth.shifted(amount), index) @@ -94,10 +117,10 @@ fn shifted(self, amount: u32) -> Region { fn from_depth(self, depth: u32) -> Region { match self { - Region::LateBound(debruijn, id) => { + Region::LateBound(debruijn, id, origin) => { Region::LateBound(ty::DebruijnIndex { depth: debruijn.depth - (depth - 1) - }, id) + }, id, origin) } Region::LateBoundAnon(debruijn, index) => { Region::LateBoundAnon(ty::DebruijnIndex { @@ -110,7 +133,7 @@ fn from_depth(self, depth: u32) -> Region { fn subst(self, params: &[hir::Lifetime], map: &NamedRegionMap) -> Option { - if let Region::EarlyBound(index, _) = self { + if let Region::EarlyBound(index, _, _) = self { params.get(index as usize).and_then(|lifetime| { map.defs.get(&lifetime.id).cloned() }) @@ -187,6 +210,9 @@ struct LifetimeContext<'a, 'tcx: 'a> { // I'm sorry. trait_ref_hack: bool, + // Used to disallow the use of in-band lifetimes in `fn` or `Fn` syntax. + is_in_fn_syntax: bool, + // List of labels in the function/method currently under analysis. labels_in_fn: Vec<(ast::Name, Span)>, @@ -280,6 +306,7 @@ pub fn krate(sess: &Session, map: &mut map, scope: ROOT_SCOPE, trait_ref_hack: false, + is_in_fn_syntax: false, labels_in_fn: vec![], xcrate_object_lifetime_defaults: DefIdMap(), }; @@ -384,6 +411,8 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty) { match ty.node { hir::TyBareFn(ref c) => { let next_early_index = self.next_early_index(); + let was_in_fn_syntax = self.is_in_fn_syntax; + self.is_in_fn_syntax = true; let scope = Scope::Binder { lifetimes: c.lifetimes.iter().map(|def| { Region::late(self.hir_map, def) @@ -397,6 +426,7 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty) { this.check_lifetime_defs(old_scope, &c.lifetimes); intravisit::walk_ty(this, ty); }); + self.is_in_fn_syntax = was_in_fn_syntax; } hir::TyTraitObject(ref bounds, ref lifetime) => { for bound in bounds { @@ -430,7 +460,7 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty) { // well-supported at the moment, so this doesn't work. // In the future, this should be fixed and this error should be removed. let def = self.map.defs.get(&lifetime.id); - if let Some(&Region::LateBound(_, def_id)) = def { + if let Some(&Region::LateBound(_, def_id, _)) = def { if let Some(node_id) = self.hir_map.as_local_node_id(def_id) { // Ensure that the parent of the def is an item, not HRTB let parent_id = self.hir_map.get_parent_node(node_id); @@ -528,6 +558,7 @@ fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl) { } fn visit_generics(&mut self, generics: &'tcx hir::Generics) { + check_mixed_explicit_and_in_band_defs(&self.sess, &generics.lifetimes); for ty_param in generics.ty_params.iter() { walk_list!(self, visit_ty_param_bound, &ty_param.bounds); if let Some(ref ty) = ty_param.default { @@ -639,6 +670,22 @@ fn desc(&self) -> &'static str { } } +fn check_mixed_explicit_and_in_band_defs( + sess: &Session, + lifetime_defs: &[hir::LifetimeDef], +) { + let oob_def = lifetime_defs.iter().find(|lt| !lt.in_band); + let in_band_def = lifetime_defs.iter().find(|lt| lt.in_band); + + if let (Some(oob_def), Some(in_band_def)) = (oob_def, in_band_def) { + struct_span_err!(sess, in_band_def.lifetime.span, E0688, + "cannot mix in-band and explicit lifetime definitions") + .span_label(in_band_def.lifetime.span, "in-band lifetime definition here") + .span_label(oob_def.lifetime.span, "explicit lifetime definition here") + .emit(); + } +} + fn signal_shadowing_problem(sess: &Session, name: ast::Name, orig: Original, shadower: Shadower) { let mut err = if let (ShadowKind::Lifetime, ShadowKind::Lifetime) = (orig.kind, shadower.kind) { // lifetime/lifetime shadowing is an error @@ -767,7 +814,7 @@ fn compute_object_lifetime_defaults(sess: &Session, hir_map: &Map) match *set { Set1::Empty => "BaseDefault".to_string(), Set1::One(Region::Static) => "'static".to_string(), - Set1::One(Region::EarlyBound(i, _)) => { + Set1::One(Region::EarlyBound(i, _, _)) => { generics.lifetimes[i as usize].lifetime.name.name().to_string() } Set1::One(_) => bug!(), @@ -837,7 +884,8 @@ fn add_bounds(set: &mut Set1, bounds: &[hir::TyParamBound]) { def.lifetime.name == name }).map_or(Set1::Many, |(i, def)| { let def_id = hir_map.local_def_id(def.lifetime.id); - Set1::One(Region::EarlyBound(i as u32, def_id)) + let origin = LifetimeDefOrigin::from_is_in_band(def.in_band); + Set1::One(Region::EarlyBound(i as u32, def_id, origin)) }) } } @@ -868,6 +916,7 @@ fn with(&mut self, wrap_scope: Scope, f: F) where map: *map, scope: &wrap_scope, trait_ref_hack: self.trait_ref_hack, + is_in_fn_syntax: self.is_in_fn_syntax, labels_in_fn, xcrate_object_lifetime_defaults, }; @@ -1020,6 +1069,28 @@ fn resolve_lifetime_ref(&mut self, lifetime_ref: &hir::Lifetime) { _ => {} } } + + // Check for fn-syntax conflicts with in-band lifetime definitions + if self.is_in_fn_syntax { + match def { + Region::EarlyBound(_, _, LifetimeDefOrigin::InBand) | + Region::LateBound(_, _, LifetimeDefOrigin::InBand) => { + struct_span_err!(self.sess, lifetime_ref.span, E0687, + "lifetimes used in `fn` or `Fn` syntax must be \ + explicitly declared using `<...>` binders") + .span_label(lifetime_ref.span, + "in-band lifetime definition") + .emit(); + }, + + Region::Static | + Region::EarlyBound(_, _, LifetimeDefOrigin::Explicit) | + Region::LateBound(_, _, LifetimeDefOrigin::Explicit) | + Region::LateBoundAnon(..) | + Region::Free(..) => {} + } + } + self.insert_lifetime(lifetime_ref, def); } else { struct_span_err!(self.sess, lifetime_ref.span, E0261, @@ -1033,8 +1104,12 @@ fn visit_segment_parameters(&mut self, def: Def, depth: usize, params: &'tcx hir::PathParameters) { + if params.parenthesized { + let was_in_fn_syntax = self.is_in_fn_syntax; + self.is_in_fn_syntax = true; self.visit_fn_like_elision(params.inputs(), Some(¶ms.bindings[0].ty)); + self.is_in_fn_syntax = was_in_fn_syntax; return; } @@ -1355,7 +1430,7 @@ fn visit_lifetime_def(&mut self, lifetime_def: &hir::LifetimeDef) { fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) { if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.id) { match lifetime { - Region::LateBound(debruijn, _) | + Region::LateBound(debruijn, _, _) | Region::LateBoundAnon(debruijn, _) if debruijn.depth < self.binder_depth => { self.have_bound_regions = true; diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 2405461741daa7c75f4334df6f5e94e7837a63d3..8b849a9e52f3a87976457557748eba659eb1dc3b 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -110,7 +110,7 @@ pub fn ast_region_to_region(&self, tcx.types.re_static } - Some(rl::Region::LateBound(debruijn, id)) => { + Some(rl::Region::LateBound(debruijn, id, _)) => { let name = lifetime_name(id); tcx.mk_region(ty::ReLateBound(debruijn, ty::BrNamed(id, name))) @@ -120,7 +120,7 @@ pub fn ast_region_to_region(&self, tcx.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(index))) } - Some(rl::Region::EarlyBound(index, id)) => { + Some(rl::Region::EarlyBound(index, id, _)) => { let name = lifetime_name(id); tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 4c6bad61b19ab961f4fcc189f92474271f8941b4..7de29868d4341afb630b979c2df226b55ba03233 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -784,7 +784,7 @@ fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) { let hir_id = self.tcx.hir.node_to_hir_id(lt.id); match self.tcx.named_region(hir_id) { Some(rl::Region::Static) | Some(rl::Region::EarlyBound(..)) => {} - Some(rl::Region::LateBound(debruijn, _)) | + Some(rl::Region::LateBound(debruijn, _, _)) | Some(rl::Region::LateBoundAnon(debruijn, _)) if debruijn.depth < self.binder_depth => {} _ => self.has_late_bound_regions = Some(lt.span), diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 42d7a31713c9041c1757e436639e5d68c6216160..e20cae036a375ddaa30960618e0202ef8950235a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1008,8 +1008,8 @@ fn clean(&self, cx: &DocContext) -> Lifetime { let hir_id = cx.tcx.hir.node_to_hir_id(self.id); let def = cx.tcx.named_region(hir_id); match def { - Some(rl::Region::EarlyBound(_, node_id)) | - Some(rl::Region::LateBound(_, node_id)) | + Some(rl::Region::EarlyBound(_, node_id, _)) | + Some(rl::Region::LateBound(_, node_id, _)) | Some(rl::Region::Free(_, node_id)) => { if let Some(lt) = cx.lt_substs.borrow().get(&node_id).cloned() { return lt; diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 383fe0092be578797f48e943ef1d93b2da44cb6e..89d1a3699e8a6dc96a6048cfeb956e961773c435 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -425,6 +425,9 @@ pub fn new() -> Features { // `crate` in paths (active, crate_in_paths, "1.23.0", Some(45477)), + + // In-band lifetime bindings (e.g. `fn foo(x: &'a u8) -> &'a u8`) + (active, in_band_lifetimes, "1.23.0", Some(44524)), ); declare_features! ( diff --git a/src/test/compile-fail/feature-gate-in_band_lifetimes.rs b/src/test/compile-fail/feature-gate-in_band_lifetimes.rs new file mode 100644 index 0000000000000000000000000000000000000000..ae1f81c2f5721fd54742b3036ebd2b7d0dcb8b60 --- /dev/null +++ b/src/test/compile-fail/feature-gate-in_band_lifetimes.rs @@ -0,0 +1,72 @@ +// 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)] + +fn foo(x: &'x u8) -> &'x u8 { x } +//~^ ERROR use of undeclared lifetime name +//~^^ ERROR use of undeclared lifetime name + +struct X<'a>(&'a u8); + +impl<'a> X<'a> { + fn inner(&self) -> &'a u8 { + self.0 + } +} + +impl<'a> X<'b> { +//~^ ERROR use of undeclared lifetime name + fn inner_2(&self) -> &'b u8 { + //~^ ERROR use of undeclared lifetime name + self.0 + } +} + +impl X<'b> { +//~^ ERROR use of undeclared lifetime name + fn inner_3(&self) -> &'b u8 { + //~^ ERROR use of undeclared lifetime name + self.0 + } +} + +struct Y(T); + +impl Y<&'a u8> { + //~^ ERROR use of undeclared lifetime name + fn inner(&self) -> &'a u8 { + //~^ ERROR use of undeclared lifetime name + self.0 + } +} + +trait MyTrait<'a> { + fn my_lifetime(&self) -> &'a u8; + fn any_lifetime() -> &'b u8; + //~^ ERROR use of undeclared lifetime name + fn borrowed_lifetime(&'b self) -> &'b u8; + //~^ ERROR use of undeclared lifetime name + //~^^ ERROR use of undeclared lifetime name +} + +impl MyTrait<'a> for Y<&'a u8> { +//~^ ERROR use of undeclared lifetime name +//~^^ ERROR use of undeclared lifetime name + fn my_lifetime(&self) -> &'a u8 { self.0 } + //~^ ERROR use of undeclared lifetime name + fn any_lifetime() -> &'b u8 { &0 } + //~^ ERROR use of undeclared lifetime name + fn borrowed_lifetime(&'b self) -> &'b u8 { &*self.0 } + //~^ ERROR use of undeclared lifetime name + //~^^ ERROR use of undeclared lifetime name +} + +fn main() {} diff --git a/src/test/run-pass/in-band-lifetimes.rs b/src/test/run-pass/in-band-lifetimes.rs new file mode 100644 index 0000000000000000000000000000000000000000..d7a837f23b190db94d15cb96802ff9abbbf57036 --- /dev/null +++ b/src/test/run-pass/in-band-lifetimes.rs @@ -0,0 +1,84 @@ +// 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(in_band_lifetimes, universal_impl_trait)] + +fn foo(x: &'x u8) -> &'x u8 { x } +fn foo2(x: &'a u8, y: &u8) -> &'a u8 { x } + +fn check_in_band_can_be_late_bound() { + let _: for<'x> fn(&'x u8, &u8) -> &'x u8 = foo2; +} + +struct ForInherentNoParams; + +impl ForInherentNoParams { + fn foo(x: &'a u32, y: &u32) -> &'a u32 { x } +} + +struct X<'a>(&'a u8); + +impl<'a> X<'a> { + fn inner(&self) -> &'a u8 { + self.0 + } + + fn same_lifetime_as_parameter(&mut self, x: &'a u8) { + self.0 = x; + } +} + +impl X<'b> { + fn inner_2(&self) -> &'b u8 { + self.0 + } + + fn reference_already_introduced_in_band_from_method_with_explicit_binders<'a>( + &'b self, x: &'a u32 + ) {} +} + +struct Y(T); + +impl Y<&'a u8> { + fn inner(&self) -> &'a u8 { + self.0 + } +} + +trait MyTrait<'a> { + fn my_lifetime(&self) -> &'a u8; + fn any_lifetime() -> &'b u8; + fn borrowed_lifetime(&'b self) -> &'b u8; + fn default_impl(&self, x: &'b u32, y: &u32) -> &'b u32 { x } + fn in_band_def_explicit_impl(&self, x: &'b u8); +} + +impl MyTrait<'a> for Y<&'a u8> { + fn my_lifetime(&self) -> &'a u8 { self.0 } + fn any_lifetime() -> &'b u8 { &0 } + fn borrowed_lifetime(&'b self) -> &'b u8 { &*self.0 } + fn in_band_def_explicit_impl<'b>(&self, x: &'b u8) {} +} + +fn test_hrtb_defined_lifetime_where(_: F) where for<'a> F: Fn(&'a u8) {} +fn test_hrtb_defined_lifetime_polytraitref(_: F) where F: for<'a> Fn(&'a u8) {} + +fn reference_in_band_from_locals(x: &'test u32) -> &'test u32 { + let y: &'test u32 = x; + y +} + +fn in_generics_in_band>(x: &T) {} +fn where_clause_in_band(x: &T) where T: MyTrait<'a> {} +fn impl_trait_in_band(x: &impl MyTrait<'a>) {} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/E0687.rs b/src/test/ui/in-band-lifetimes/E0687.rs new file mode 100644 index 0000000000000000000000000000000000000000..57b355cbb6a3174338945684624af331522f8a60 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/E0687.rs @@ -0,0 +1,26 @@ +// 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(in_band_lifetimes)] + +fn foo(x: fn(&'a u32)) {} + +fn bar(x: &Fn(&'a u32)) {} + +fn baz(x: fn(&'a u32), y: &'a u32) {} + +struct Foo<'a> { x: &'a u32 } + +impl Foo<'a> { + fn bar(&self, x: fn(&'a u32)) {} +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/E0687.stderr b/src/test/ui/in-band-lifetimes/E0687.stderr new file mode 100644 index 0000000000000000000000000000000000000000..14b513fc52aa2ab21105ed05cb650f8c15aa6f23 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/E0687.stderr @@ -0,0 +1,26 @@ +error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders + --> $DIR/E0687.rs:14:15 + | +14 | fn foo(x: fn(&'a u32)) {} + | ^^ in-band lifetime definition + +error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders + --> $DIR/E0687.rs:16:16 + | +16 | fn bar(x: &Fn(&'a u32)) {} + | ^^ in-band lifetime definition + +error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders + --> $DIR/E0687.rs:18:15 + | +18 | fn baz(x: fn(&'a u32), y: &'a u32) {} + | ^^ in-band lifetime definition + +error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders + --> $DIR/E0687.rs:23:26 + | +23 | fn bar(&self, x: fn(&'a u32)) {} + | ^^ in-band lifetime definition + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/in-band-lifetimes/E0687_where.rs b/src/test/ui/in-band-lifetimes/E0687_where.rs new file mode 100644 index 0000000000000000000000000000000000000000..6f04720dd4ace59694451f48c8628c65ef772988 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/E0687_where.rs @@ -0,0 +1,18 @@ +// 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(in_band_lifetimes, universal_impl_trait)] + +fn bar(x: &F) where F: Fn(&'a u32) {} + +fn baz(x: &impl Fn(&'a u32)) {} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/E0687_where.stderr b/src/test/ui/in-band-lifetimes/E0687_where.stderr new file mode 100644 index 0000000000000000000000000000000000000000..db50076fe7c01539857a2075f42e81f701e571be --- /dev/null +++ b/src/test/ui/in-band-lifetimes/E0687_where.stderr @@ -0,0 +1,14 @@ +error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders + --> $DIR/E0687_where.rs:14:31 + | +14 | fn bar(x: &F) where F: Fn(&'a u32) {} + | ^^ in-band lifetime definition + +error[E0687]: lifetimes used in `fn` or `Fn` syntax must be explicitly declared using `<...>` binders + --> $DIR/E0687_where.rs:16:21 + | +16 | fn baz(x: &impl Fn(&'a u32)) {} + | ^^ in-band lifetime definition + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/in-band-lifetimes/E0688.rs b/src/test/ui/in-band-lifetimes/E0688.rs new file mode 100644 index 0000000000000000000000000000000000000000..e6fe4e138e4746bf49b5f4cc752170cc517f2581 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/E0688.rs @@ -0,0 +1,26 @@ +// 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(in_band_lifetimes)] + +fn foo<'a>(x: &'a u32, y: &'b u32) {} + +struct Foo<'a> { x: &'a u32 } + +impl Foo<'a> { + fn bar<'b>(x: &'a u32, y: &'b u32, z: &'c u32) {} +} + +impl<'b> Foo<'a> { + fn baz() {} +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/E0688.stderr b/src/test/ui/in-band-lifetimes/E0688.stderr new file mode 100644 index 0000000000000000000000000000000000000000..1289e7fd01039b3b8aecde94296012e194d1974c --- /dev/null +++ b/src/test/ui/in-band-lifetimes/E0688.stderr @@ -0,0 +1,26 @@ +error[E0688]: cannot mix in-band and explicit lifetime definitions + --> $DIR/E0688.rs:14:28 + | +14 | fn foo<'a>(x: &'a u32, y: &'b u32) {} + | -- ^^ in-band lifetime definition here + | | + | explicit lifetime definition here + +error[E0688]: cannot mix in-band and explicit lifetime definitions + --> $DIR/E0688.rs:19:44 + | +19 | fn bar<'b>(x: &'a u32, y: &'b u32, z: &'c u32) {} + | -- ^^ in-band lifetime definition here + | | + | explicit lifetime definition here + +error[E0688]: cannot mix in-band and explicit lifetime definitions + --> $DIR/E0688.rs:22:14 + | +22 | impl<'b> Foo<'a> { + | -- ^^ in-band lifetime definition here + | | + | explicit lifetime definition here + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/in-band-lifetimes/mismatched.rs b/src/test/ui/in-band-lifetimes/mismatched.rs new file mode 100644 index 0000000000000000000000000000000000000000..6b0a3fedfc5cc946897dc744eb49dbf8cb4ca5f8 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mismatched.rs @@ -0,0 +1,18 @@ +// 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(in_band_lifetimes)] + +fn foo(x: &'a u32, y: &u32) -> &'a u32 { y } + +fn foo2(x: &'a u32, y: &'b u32) -> &'a u32 { y } + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/mismatched.stderr b/src/test/ui/in-band-lifetimes/mismatched.stderr new file mode 100644 index 0000000000000000000000000000000000000000..bbc25869f46c06045231719ba7104a6ebd8482dd --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mismatched.stderr @@ -0,0 +1,18 @@ +error[E0621]: explicit lifetime required in the type of `y` + --> $DIR/mismatched.rs:14:42 + | +14 | fn foo(x: &'a u32, y: &u32) -> &'a u32 { y } + | - ^ lifetime `'a` required + | | + | consider changing the type of `y` to `&'a u32` + +error[E0623]: lifetime mismatch + --> $DIR/mismatched.rs:16:46 + | +16 | fn foo2(x: &'a u32, y: &'b u32) -> &'a u32 { y } + | ------- ------- ^ ...but data from `y` is returned here + | | + | this parameter and the return type are declared with different lifetimes... + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait.rs b/src/test/ui/in-band-lifetimes/mismatched_trait.rs new file mode 100644 index 0000000000000000000000000000000000000000..2e31677040425d217cb4b5c9c684270f4b12fb2b --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mismatched_trait.rs @@ -0,0 +1,20 @@ +// 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(in_band_lifetimes)] + +trait Get { + fn baz(&self, x: &'a u32, y: &u32) -> &'a u32 { + y + } +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait.stderr new file mode 100644 index 0000000000000000000000000000000000000000..315e881bf0738f47d46b25f5c87132ef019dfcad --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mismatched_trait.stderr @@ -0,0 +1,10 @@ +error[E0621]: explicit lifetime required in the type of `y` + --> $DIR/mismatched_trait.rs:16:9 + | +15 | fn baz(&self, x: &'a u32, y: &u32) -> &'a u32 { + | - consider changing the type of `y` to `&'a u32` +16 | y + | ^ lifetime `'a` required + +error: aborting due to previous error + diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.rs b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.rs new file mode 100644 index 0000000000000000000000000000000000000000..1be32e5c51e717bf7f63c3520bfcf06c682f4af7 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.rs @@ -0,0 +1,24 @@ +// 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(in_band_lifetimes)] + +trait Get { + fn foo(&self, x: &'a u32, y: &u32) -> &'a u32; +} + +impl Get for i32 { + fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { + x + } +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr new file mode 100644 index 0000000000000000000000000000000000000000..d1f3ae450f8b0ab8af0dadc960f864b083e0d733 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mismatched_trait_impl.stderr @@ -0,0 +1,39 @@ +error[E0495]: cannot infer an appropriate lifetime for lifetime parameter 'a in generic type due to conflicting requirements + --> $DIR/mismatched_trait_impl.rs:19:5 + | +19 | / fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { +20 | | x +21 | | } + | |_____^ + | +note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the method body at 19:5... + --> $DIR/mismatched_trait_impl.rs:19:5 + | +19 | / fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { +20 | | x +21 | | } + | |_____^ +note: ...so that method type is compatible with trait (expected fn(&i32, &'a u32, &u32) -> &'a u32, found fn(&i32, &u32, &u32) -> &u32) + --> $DIR/mismatched_trait_impl.rs:19:5 + | +19 | / fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { +20 | | x +21 | | } + | |_____^ +note: but, the lifetime must be valid for the lifetime 'a as defined on the method body at 19:5... + --> $DIR/mismatched_trait_impl.rs:19:5 + | +19 | / fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { +20 | | x +21 | | } + | |_____^ +note: ...so that method type is compatible with trait (expected fn(&i32, &'a u32, &u32) -> &'a u32, found fn(&i32, &u32, &u32) -> &u32) + --> $DIR/mismatched_trait_impl.rs:19:5 + | +19 | / fn foo(&self, x: &u32, y: &'a u32) -> &'a u32 { +20 | | x +21 | | } + | |_____^ + +error: aborting due to previous error + diff --git a/src/test/ui/in-band-lifetimes/mut_while_borrow.rs b/src/test/ui/in-band-lifetimes/mut_while_borrow.rs new file mode 100644 index 0000000000000000000000000000000000000000..fabf531968a4c031da4e979e75b6476e64c38a5e --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mut_while_borrow.rs @@ -0,0 +1,21 @@ +// 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(in_band_lifetimes)] + +fn foo(x: &'a u32) -> &'a u32 { x } + +fn main() { + let mut p = 3; + let r = foo(&p); + p += 1; + println!("{}", r); +} diff --git a/src/test/ui/in-band-lifetimes/mut_while_borrow.stderr b/src/test/ui/in-band-lifetimes/mut_while_borrow.stderr new file mode 100644 index 0000000000000000000000000000000000000000..4800e39269f0a5756b7e3f3305e3584beddfdd32 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/mut_while_borrow.stderr @@ -0,0 +1,10 @@ +error[E0506]: cannot assign to `p` because it is borrowed + --> $DIR/mut_while_borrow.rs:19:5 + | +18 | let r = foo(&p); + | - borrow of `p` occurs here +19 | p += 1; + | ^^^^^^ assignment to borrowed `p` occurs here + +error: aborting due to previous error + diff --git a/src/test/ui/in-band-lifetimes/no_in_band_in_struct.rs b/src/test/ui/in-band-lifetimes/no_in_band_in_struct.rs new file mode 100644 index 0000000000000000000000000000000000000000..b421e16092c2b48bdebc3824736fa8ed76a4074a --- /dev/null +++ b/src/test/ui/in-band-lifetimes/no_in_band_in_struct.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(in_band_lifetimes)] + +struct Foo { + x: &'test u32, +} + +enum Bar { + Baz(&'test u32), +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr b/src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr new file mode 100644 index 0000000000000000000000000000000000000000..7ffbfbd89b672531bf94a22e7248221828d5191a --- /dev/null +++ b/src/test/ui/in-band-lifetimes/no_in_band_in_struct.stderr @@ -0,0 +1,14 @@ +error[E0261]: use of undeclared lifetime name `'test` + --> $DIR/no_in_band_in_struct.rs:15:9 + | +15 | x: &'test u32, + | ^^^^^ undeclared lifetime + +error[E0261]: use of undeclared lifetime name `'test` + --> $DIR/no_in_band_in_struct.rs:19:10 + | +19 | Baz(&'test u32), + | ^^^^^ undeclared lifetime + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.rs b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.rs new file mode 100644 index 0000000000000000000000000000000000000000..a7c9d4ece9569e3c584a7295e42494cde3f376d6 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.rs @@ -0,0 +1,23 @@ +// 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(in_band_lifetimes)] + +fn foo(x: &u32) { + let y: &'test u32 = x; +} + +fn foo2(x: &u32) {} +fn bar() { + let y: fn(&'test u32) = foo2; +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr new file mode 100644 index 0000000000000000000000000000000000000000..a0c2e8199694a045f752e8f61bb4ac60308eb4d6 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/no_introducing_in_band_in_locals.stderr @@ -0,0 +1,14 @@ +error[E0261]: use of undeclared lifetime name `'test` + --> $DIR/no_introducing_in_band_in_locals.rs:15:13 + | +15 | let y: &'test u32 = x; + | ^^^^^ undeclared lifetime + +error[E0261]: use of undeclared lifetime name `'test` + --> $DIR/no_introducing_in_band_in_locals.rs:20:16 + | +20 | let y: fn(&'test u32) = foo2; + | ^^^^^ undeclared lifetime + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/in-band-lifetimes/shadow.rs b/src/test/ui/in-band-lifetimes/shadow.rs new file mode 100644 index 0000000000000000000000000000000000000000..a9053d09f6c60499e94527d31acc86ec485117f5 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/shadow.rs @@ -0,0 +1,21 @@ +// 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(in_band_lifetimes)] + +struct Foo(T); + +impl Foo<&'s u8> { + fn bar<'s>(&self, x: &'s u8) {} + fn baz(x: for<'s> fn(&'s u32)) {} +} + +fn main() {} diff --git a/src/test/ui/in-band-lifetimes/shadow.stderr b/src/test/ui/in-band-lifetimes/shadow.stderr new file mode 100644 index 0000000000000000000000000000000000000000..f05b76fbea4c564471a9e2778401de43e3de1985 --- /dev/null +++ b/src/test/ui/in-band-lifetimes/shadow.stderr @@ -0,0 +1,19 @@ +error[E0496]: lifetime name `'s` shadows a lifetime name that is already in scope + --> $DIR/shadow.rs:17:12 + | +16 | impl Foo<&'s u8> { + | -- first declared here +17 | fn bar<'s>(&self, x: &'s u8) {} + | ^^ lifetime 's already in scope + +error[E0496]: lifetime name `'s` shadows a lifetime name that is already in scope + --> $DIR/shadow.rs:18:19 + | +16 | impl Foo<&'s u8> { + | -- first declared here +17 | fn bar<'s>(&self, x: &'s u8) {} +18 | fn baz(x: for<'s> fn(&'s u32)) {} + | ^^ lifetime 's already in scope + +error: aborting due to 2 previous errors +