From d1a86aa0313e775d87e0c6171dfe1ffbdc63dce8 Mon Sep 17 00:00:00 2001 From: Douglas Campos Date: Tue, 7 Nov 2017 23:43:07 -0500 Subject: [PATCH 1/3] add failing test for the feature gate --- .../feature-gate/lifetime_elision.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/test/compile-fail/feature-gate/lifetime_elision.rs diff --git a/src/test/compile-fail/feature-gate/lifetime_elision.rs b/src/test/compile-fail/feature-gate/lifetime_elision.rs new file mode 100644 index 0000000000000..53b4cb2867ea6 --- /dev/null +++ b/src/test/compile-fail/feature-gate/lifetime_elision.rs @@ -0,0 +1,17 @@ +// 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. + +#![feature(underscore_lifetimes)] + +trait Foo{} + +impl Foo for &'_ str { } //~ ERROR missing lifetime specifier + +fn main(){} From e2ff27e2f173eb4905a476f580b13ac3da6b513b Mon Sep 17 00:00:00 2001 From: Douglas Campos Date: Fri, 24 Nov 2017 14:29:37 -0500 Subject: [PATCH 2/3] track lifetime elision behavior in HIR --- src/librustc/hir/lowering.rs | 157 ++++++++++++++++++++++++++--------- 1 file changed, 119 insertions(+), 38 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 7fd6f4a8b4278..61cfbd00e5337 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -131,6 +131,30 @@ pub struct LoweringContext<'a> { current_hir_id_owner: Vec<(DefIndex, u32)>, item_local_id_counters: NodeMap, node_id_to_hir_id: IndexVec, + + /// How to handle elided lifetimes; modified as we + /// descend the tree. + lifetime_elision_behavior: LifetimeElisionBehavior, +} + +/// What to do when we encounter an elided lifetime, +/// either because nothing was written or because `'_` +/// was written. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum LifetimeElisionBehavior { + /// Generate an early-bound lifetime on the impl + /// header. Used in impl headers: + /// + /// ``` + /// impl Foo for &str { } + /// // ^ this lifetime here + /// ``` + GenerateEarlyBound, + + /// Generate a reference to the special elided + /// lifetime name, this will then be handled + /// by `middle::resolve_lifetimes`. + GenerateElided, } pub trait Resolver { @@ -200,6 +224,7 @@ pub fn lower_crate(sess: &Session, lifetimes_to_define: Vec::new(), is_collecting_in_band_lifetimes: false, in_scope_lifetimes: Vec::new(), + lifetime_elision_behavior: LifetimeElisionBehavior::GenerateElided, }.lower_crate(krate) } @@ -410,6 +435,24 @@ impl<'a> LoweringContext<'a> { } } + /// Shorthand for enabling elision. + fn with_elision_permitted(&mut self, f: F) -> R + where F: FnOnce(&mut Self) -> R + { + self.with_lifetime_elision_behavior(LifetimeElisionBehavior::GenerateElided, f) + } + + /// Updates `lifetime_elision_behavior` to `behavior` for the duration of `f()`, + /// resetting it back to its old value afterwards. + fn with_lifetime_elision_behavior(&mut self, behavior: LifetimeElisionBehavior, f: F) -> R + where F: FnOnce(&mut Self) -> R + { + let old_lifetime_elision_behavior = mem::replace(&mut self.lifetime_elision_behavior, behavior); + let result = f(self); + self.lifetime_elision_behavior = old_lifetime_elision_behavior; + result + } + fn with_hir_id_owner(&mut self, owner: NodeId, f: F) where F: FnOnce(&mut Self) { @@ -862,13 +905,15 @@ impl<'a> LoweringContext<'a> { } TyKind::BareFn(ref f) => { self.with_in_scope_lifetime_defs(&f.lifetimes, |this| + this.with_elision_permitted(|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) => { @@ -916,7 +961,10 @@ impl<'a> LoweringContext<'a> { } }).collect(); let lifetime_bound = lifetime_bound.unwrap_or_else(|| { - self.elided_lifetime(t.span) + // The lifetime object bound is scoped to this trait object type, so we want to + // permit elision. + self.with_elision_permitted( + |this| this.elided_lifetime(t.span)) }); hir::TyTraitObject(bounds, lifetime_bound) } @@ -1342,25 +1390,27 @@ impl<'a> LoweringContext<'a> { data: &ParenthesizedParameterData) -> (hir::PathParameters, bool) { const DISALLOWED: ImplTraitContext = ImplTraitContext::Disallowed; - let &ParenthesizedParameterData { ref inputs, ref output, span } = data; - let inputs = inputs.iter().map(|ty| self.lower_ty(ty, DISALLOWED)).collect(); - let mk_tup = |this: &mut Self, tys, span| { - let LoweredNodeId { node_id, hir_id } = this.next_id(); - P(hir::Ty { node: hir::TyTup(tys), id: node_id, hir_id, span }) - }; + self.with_elision_permitted(|this| { + let &ParenthesizedParameterData { ref inputs, ref output, span } = data; + let inputs = inputs.iter().map(|ty| this.lower_ty(ty, DISALLOWED)).collect(); + let mk_tup = |this: &mut Self, tys, span| { + let LoweredNodeId { node_id, hir_id } = this.next_id(); + P(hir::Ty { node: hir::TyTup(tys), id: node_id, hir_id, span }) + }; - (hir::PathParameters { - lifetimes: hir::HirVec::new(), - types: hir_vec![mk_tup(self, inputs, span)], - bindings: hir_vec![hir::TypeBinding { - id: self.next_id().node_id, - name: Symbol::intern(FN_OUTPUT_NAME), - ty: output.as_ref().map(|ty| self.lower_ty(&ty, DISALLOWED)) - .unwrap_or_else(|| mk_tup(self, hir::HirVec::new(), span)), - span: output.as_ref().map_or(span, |ty| ty.span), - }], - parenthesized: true, - }, false) + (hir::PathParameters { + lifetimes: hir::HirVec::new(), + types: hir_vec![mk_tup(this, inputs, span)], + bindings: hir_vec![hir::TypeBinding { + id: this.next_id().node_id, + name: Symbol::intern(FN_OUTPUT_NAME), + ty: output.as_ref().map(|ty| this.lower_ty(&ty, DISALLOWED)) + .unwrap_or_else(|| mk_tup(this, hir::HirVec::new(), span)), + span: output.as_ref().map_or(span, |ty| ty.span), + }], + parenthesized: true, + }, false) + }) } fn lower_local(&mut self, l: &Local) -> P { @@ -1906,6 +1956,33 @@ impl<'a> LoweringContext<'a> { hir::ItemAutoImpl(self.lower_unsafety(unsafety), trait_ref) } + ItemKind::Impl(..) => { + self.with_lifetime_elision_behavior( + LifetimeElisionBehavior::GenerateEarlyBound, + |this| this.lower_impl(id, i)) + } + ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref items) => { + let bounds = self.lower_bounds(bounds, ImplTraitContext::Disallowed); + let items = items.iter().map(|item| self.lower_trait_item_ref(item)).collect(); + hir::ItemTrait(self.lower_is_auto(is_auto), + self.lower_unsafety(unsafety), + self.lower_generics(generics), + bounds, + items) + } + ItemKind::MacroDef(..) | ItemKind::Mac(..) => panic!("Shouldn't still be around"), + } + + // [1] `defaultness.has_value()` is never called for an `impl`, always `true` in order to + // not cause an assertion failure inside the `lower_defaultness` function + } + + /// Lower an AST impl into a HIR impl. Assumes that the caller has setup + /// the elision behavior for us. + fn lower_impl(&mut self, id: NodeId, i: &ItemKind) -> hir::Item_ { + assert_eq!(self.lifetime_elision_behavior, + LifetimeElisionBehavior::GenerateEarlyBound); + match *i { ItemKind::Impl(unsafety, polarity, defaultness, @@ -1947,20 +2024,9 @@ impl<'a> LoweringContext<'a> { lowered_ty, new_impl_items) } - ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref items) => { - let bounds = self.lower_bounds(bounds, ImplTraitContext::Disallowed); - let items = items.iter().map(|item| self.lower_trait_item_ref(item)).collect(); - hir::ItemTrait(self.lower_is_auto(is_auto), - self.lower_unsafety(unsafety), - self.lower_generics(generics), - bounds, - items) - } - ItemKind::MacroDef(..) | ItemKind::Mac(..) => panic!("Shouldn't still be around"), - } - // [1] `defaultness.has_value()` is never called for an `impl`, always `true` in order to - // not cause an assertion failure inside the `lower_defaultness` function + _ => bug!("lower_impl invoked with non-impl") + } } fn lower_trait_item(&mut self, i: &TraitItem) -> hir::TraitItem { @@ -3498,7 +3564,14 @@ impl<'a> LoweringContext<'a> { // so the `Ty` itself needs a different one. id = self.next_id(); - hir::TyTraitObject(hir_vec![principal], self.elided_lifetime(span)) + // Trait object lifetimes are scoped to the + // trait object type and hence can always be + // elided. + let lifetime = + self.with_elision_permitted( + |this| this.elided_lifetime(span)); + + hir::TyTraitObject(hir_vec![principal], lifetime) } else { hir::TyPath(hir::QPath::Resolved(None, path)) } @@ -3509,10 +3582,18 @@ impl<'a> LoweringContext<'a> { } fn elided_lifetime(&mut self, span: Span) -> hir::Lifetime { - hir::Lifetime { - id: self.next_id().node_id, - span, - name: hir::LifetimeName::Implicit, + match self.lifetime_elision_behavior { + LifetimeElisionBehavior::GenerateElided => { + hir::Lifetime { + id: self.next_id().node_id, + span, + name: hir::LifetimeName::Implicit, + } + } + + LifetimeElisionBehavior::GenerateEarlyBound => { + self.sess.diagnostic().span_bug(span, "elided lifetime in impl header?") + } } } } From 0d92d6c8295bee07c96d0c1ae607ea934c45c2dc Mon Sep 17 00:00:00 2001 From: Douglas Campos Date: Fri, 24 Nov 2017 16:06:27 -0500 Subject: [PATCH 3/3] make tidy happy --- src/librustc/hir/lowering.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 61cfbd00e5337..d25466729b4f7 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -447,7 +447,8 @@ impl<'a> LoweringContext<'a> { fn with_lifetime_elision_behavior(&mut self, behavior: LifetimeElisionBehavior, f: F) -> R where F: FnOnce(&mut Self) -> R { - let old_lifetime_elision_behavior = mem::replace(&mut self.lifetime_elision_behavior, behavior); + let old_lifetime_elision_behavior = mem::replace( + &mut self.lifetime_elision_behavior, behavior); let result = f(self); self.lifetime_elision_behavior = old_lifetime_elision_behavior; result @@ -1958,7 +1959,7 @@ impl<'a> LoweringContext<'a> { } ItemKind::Impl(..) => { self.with_lifetime_elision_behavior( - LifetimeElisionBehavior::GenerateEarlyBound, + LifetimeElisionBehavior::GenerateEarlyBound, |this| this.lower_impl(id, i)) } ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref items) => { @@ -1980,7 +1981,7 @@ impl<'a> LoweringContext<'a> { /// Lower an AST impl into a HIR impl. Assumes that the caller has setup /// the elision behavior for us. fn lower_impl(&mut self, id: NodeId, i: &ItemKind) -> hir::Item_ { - assert_eq!(self.lifetime_elision_behavior, + assert_eq!(self.lifetime_elision_behavior, LifetimeElisionBehavior::GenerateEarlyBound); match *i { ItemKind::Impl(unsafety, @@ -3564,7 +3565,7 @@ impl<'a> LoweringContext<'a> { // so the `Ty` itself needs a different one. id = self.next_id(); - // Trait object lifetimes are scoped to the + // Trait object lifetimes are scoped to the // trait object type and hence can always be // elided. let lifetime =