diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 2878ff5e2846e..b51a7d4104ab9 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -327,6 +327,69 @@ struct ListNode { This works because `Box` is a pointer, so its size is well-known. "##, +E0106: r##" +This error indicates that a lifetime is missing from a type. If it is an error +inside a function signature, the problem may be with failing to adhere to the +lifetime elision rules (see below). + +Here are some simple examples of where you'll run into this error: + +```compile_fail,E0106 +struct Foo { x: &bool } // error +struct Foo<'a> { x: &'a bool } // correct + +enum Bar { A(u8), B(&bool), } // error +enum Bar<'a> { A(u8), B(&'a bool), } // correct + +type MyStr = &str; // error +type MyStr<'a> = &'a str; // correct +``` + +Lifetime elision is a special, limited kind of inference for lifetimes in +function signatures which allows you to leave out lifetimes in certain cases. +For more background on lifetime elision see [the book][book-le]. + +The lifetime elision rules require that any function signature with an elided +output lifetime must either have + + - exactly one input lifetime + - or, multiple input lifetimes, but the function must also be a method with a + `&self` or `&mut self` receiver + +In the first case, the output lifetime is inferred to be the same as the unique +input lifetime. In the second case, the lifetime is instead inferred to be the +same as the lifetime on `&self` or `&mut self`. + +Here are some examples of elision errors: + +```compile_fail,E0106 +// error, no input lifetimes +fn foo() -> &str { } + +// error, `x` and `y` have distinct lifetimes inferred +fn bar(x: &str, y: &str) -> &str { } + +// error, `y`'s lifetime is inferred to be distinct from `x`'s +fn baz<'a>(x: &'a str, y: &str) -> &str { } +``` + +Here's an example that is currently an error, but may work in a future version +of Rust: + +```compile_fail,E0106 +struct Foo<'a>(&'a str); + +trait Quux { } +impl Quux for Foo { } +``` + +Lifetime elision in implementation headers was part of the lifetime elision +RFC. It is, however, [currently unimplemented][iss15872]. + +[book-le]: https://doc.rust-lang.org/nightly/book/lifetimes.html#lifetime-elision +[iss15872]: https://github.com/rust-lang/rust/issues/15872 +"##, + E0109: r##" You tried to give a type parameter to a type which doesn't need it. Erroneous code example: diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index d4095c6875c1d..4b3e0d29101e4 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -301,7 +301,7 @@ pub trait Visitor<'v> : Sized { fn visit_ty_param_bound(&mut self, bounds: &'v TyParamBound) { walk_ty_param_bound(self, bounds) } - fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef, m: &'v TraitBoundModifier) { + fn visit_poly_trait_ref(&mut self, t: &'v PolyTraitRef, m: TraitBoundModifier) { walk_poly_trait_ref(self, t, m) } fn visit_variant_data(&mut self, @@ -421,7 +421,7 @@ pub fn walk_lifetime_def<'v, V: Visitor<'v>>(visitor: &mut V, lifetime_def: &'v pub fn walk_poly_trait_ref<'v, V>(visitor: &mut V, trait_ref: &'v PolyTraitRef, - _modifier: &'v TraitBoundModifier) + _modifier: TraitBoundModifier) where V: Visitor<'v> { walk_list!(visitor, visit_lifetime_def, &trait_ref.bound_lifetimes); @@ -547,8 +547,8 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) { TyPtr(ref mutable_type) => { visitor.visit_ty(&mutable_type.ty) } - TyRptr(ref opt_lifetime, ref mutable_type) => { - walk_list!(visitor, visit_lifetime, opt_lifetime); + TyRptr(ref lifetime, ref mutable_type) => { + visitor.visit_lifetime(lifetime); visitor.visit_ty(&mutable_type.ty) } TyNever => {}, @@ -566,8 +566,11 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) { visitor.visit_ty(ty); visitor.visit_nested_body(length) } - TyTraitObject(ref bounds) => { - walk_list!(visitor, visit_ty_param_bound, bounds); + TyTraitObject(ref bounds, ref lifetime) => { + for bound in bounds { + visitor.visit_poly_trait_ref(bound, TraitBoundModifier::None); + } + visitor.visit_lifetime(lifetime); } TyImplTrait(ref bounds) => { walk_list!(visitor, visit_ty_param_bound, bounds); @@ -695,7 +698,7 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, foreign_item: &'v pub fn walk_ty_param_bound<'v, V: Visitor<'v>>(visitor: &mut V, bound: &'v TyParamBound) { match *bound { - TraitTyParamBound(ref typ, ref modifier) => { + TraitTyParamBound(ref typ, modifier) => { visitor.visit_poly_trait_ref(typ, modifier); } RegionTyParamBound(ref lifetime) => { diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 88d461cab9f40..8a4acb3d03880 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -41,12 +41,12 @@ // in the HIR, especially for multiple identifiers. use hir; -use hir::map::Definitions; +use hir::map::{Definitions, DefKey}; use hir::map::definitions::DefPathData; use hir::def_id::{DefIndex, DefId}; use hir::def::{Def, PathResolution}; use session::Session; -use util::nodemap::{NodeMap, FxHashMap}; +use util::nodemap::{DefIdMap, NodeMap, FxHashMap}; use std::collections::BTreeMap; use std::iter; @@ -78,6 +78,8 @@ pub struct LoweringContext<'a> { trait_items: BTreeMap, impl_items: BTreeMap, bodies: FxHashMap, + + type_def_lifetime_params: DefIdMap, } pub trait Resolver { @@ -110,6 +112,7 @@ pub fn lower_crate(sess: &Session, trait_items: BTreeMap::new(), impl_items: BTreeMap::new(), bodies: FxHashMap(), + type_def_lifetime_params: DefIdMap(), }.lower_crate(krate) } @@ -123,24 +126,33 @@ enum ParamMode { impl<'a> LoweringContext<'a> { fn lower_crate(mut self, c: &Crate) -> hir::Crate { - self.lower_items(c); - let module = self.lower_mod(&c.module); - let attrs = self.lower_attrs(&c.attrs); - let exported_macros = c.exported_macros.iter().map(|m| self.lower_macro_def(m)).collect(); + /// Full-crate AST visitor that inserts into a fresh + /// `LoweringContext` any information that may be + /// needed from arbitrary locations in the crate. + /// E.g. The number of lifetime generic parameters + /// declared for every type and trait definition. + struct MiscCollector<'lcx, 'interner: 'lcx> { + lctx: &'lcx mut LoweringContext<'interner>, + } - hir::Crate { - module: module, - attrs: attrs, - span: c.span, - exported_macros: exported_macros, - items: self.items, - trait_items: self.trait_items, - impl_items: self.impl_items, - bodies: self.bodies, + impl<'lcx, 'interner> Visitor<'lcx> for MiscCollector<'lcx, 'interner> { + fn visit_item(&mut self, item: &'lcx Item) { + match item.node { + ItemKind::Struct(_, ref generics) | + ItemKind::Union(_, ref generics) | + ItemKind::Enum(_, ref generics) | + ItemKind::Ty(_, ref generics) | + ItemKind::Trait(_, ref generics, ..) => { + let def_id = self.lctx.resolver.definitions().local_def_id(item.id); + let count = generics.lifetimes.len(); + self.lctx.type_def_lifetime_params.insert(def_id, count); + } + _ => {} + } + visit::walk_item(self, item); + } } - } - fn lower_items(&mut self, c: &Crate) { struct ItemLowerer<'lcx, 'interner: 'lcx> { lctx: &'lcx mut LoweringContext<'interner>, } @@ -167,8 +179,23 @@ impl<'a> LoweringContext<'a> { } } - let mut item_lowerer = ItemLowerer { lctx: self }; - visit::walk_crate(&mut item_lowerer, c); + visit::walk_crate(&mut MiscCollector { lctx: &mut self }, c); + visit::walk_crate(&mut ItemLowerer { lctx: &mut self }, c); + + let module = self.lower_mod(&c.module); + let attrs = self.lower_attrs(&c.attrs); + let exported_macros = c.exported_macros.iter().map(|m| self.lower_macro_def(m)).collect(); + + hir::Crate { + module: module, + attrs: attrs, + span: c.span, + exported_macros: exported_macros, + items: self.items, + trait_items: self.trait_items, + impl_items: self.impl_items, + bodies: self.bodies, + } } fn record_body(&mut self, value: hir::Expr, decl: Option<&FnDecl>) @@ -232,6 +259,14 @@ impl<'a> LoweringContext<'a> { result } + fn def_key(&mut self, id: DefId) -> DefKey { + if id.is_local() { + self.resolver.definitions().def_key(id.index) + } else { + self.sess.cstore.def_key(id) + } + } + fn lower_opt_sp_ident(&mut self, o_id: Option>) -> Option> { o_id.map(|sp_ident| respan(sp_ident.span, sp_ident.node.name)) } @@ -279,7 +314,12 @@ impl<'a> LoweringContext<'a> { TyKind::Slice(ref ty) => hir::TySlice(self.lower_ty(ty)), TyKind::Ptr(ref mt) => hir::TyPtr(self.lower_mt(mt)), TyKind::Rptr(ref region, ref mt) => { - hir::TyRptr(self.lower_opt_lifetime(region), self.lower_mt(mt)) + let span = Span { hi: t.span.lo, ..t.span }; + let lifetime = match *region { + Some(ref lt) => self.lower_lifetime(lt), + None => self.elided_lifetime(span) + }; + hir::TyRptr(lifetime, self.lower_mt(mt)) } TyKind::BareFn(ref f) => { hir::TyBareFn(P(hir::BareFnTy { @@ -297,7 +337,8 @@ impl<'a> LoweringContext<'a> { return self.lower_ty(ty); } TyKind::Path(ref qself, ref path) => { - hir::TyPath(self.lower_qpath(t.id, qself, path, ParamMode::Explicit)) + let qpath = self.lower_qpath(t.id, qself, path, ParamMode::Explicit); + return self.ty_path(t.id, t.span, qpath); } TyKind::ImplicitSelf => { hir::TyPath(hir::QPath::Resolved(None, P(hir::Path { @@ -319,7 +360,23 @@ impl<'a> LoweringContext<'a> { hir::TyTypeof(self.record_body(expr, None)) } TyKind::TraitObject(ref bounds) => { - hir::TyTraitObject(self.lower_bounds(bounds)) + let mut lifetime_bound = None; + let bounds = bounds.iter().filter_map(|bound| { + match *bound { + TraitTyParamBound(ref ty, TraitBoundModifier::None) => { + Some(self.lower_poly_trait_ref(ty)) + } + TraitTyParamBound(_, TraitBoundModifier::Maybe) => None, + RegionTyParamBound(ref lifetime) => { + lifetime_bound = Some(self.lower_lifetime(lifetime)); + None + } + } + }).collect(); + let lifetime_bound = lifetime_bound.unwrap_or_else(|| { + self.elided_lifetime(t.span) + }); + hir::TyTraitObject(bounds, lifetime_bound) } TyKind::ImplTrait(ref bounds) => { hir::TyImplTrait(self.lower_bounds(bounds)) @@ -377,7 +434,40 @@ impl<'a> LoweringContext<'a> { } _ => param_mode }; - self.lower_path_segment(segment, param_mode) + + // Figure out if this is a type/trait segment, + // which may need lifetime elision performed. + let parent_def_id = |this: &mut Self, def_id: DefId| { + DefId { + krate: def_id.krate, + index: this.def_key(def_id).parent.expect("missing parent") + } + }; + let type_def_id = match resolution.base_def { + Def::AssociatedTy(def_id) if i + 2 == proj_start => { + Some(parent_def_id(self, def_id)) + } + Def::Variant(def_id) if i + 1 == proj_start => { + Some(parent_def_id(self, def_id)) + } + Def::Struct(def_id) | + Def::Union(def_id) | + Def::Enum(def_id) | + Def::TyAlias(def_id) | + Def::Trait(def_id) if i + 1 == proj_start => Some(def_id), + _ => None + }; + + let num_lifetimes = type_def_id.map_or(0, |def_id| { + if let Some(&n) = self.type_def_lifetime_params.get(&def_id) { + return n; + } + assert!(!def_id.is_local()); + let (n, _) = self.sess.cstore.item_generics_own_param_counts(def_id); + self.type_def_lifetime_params.insert(def_id, n); + n + }); + self.lower_path_segment(p.span, segment, param_mode, num_lifetimes) }).collect(), span: p.span, }); @@ -397,7 +487,8 @@ impl<'a> LoweringContext<'a> { // Otherwise, the base path is an implicit `Self` type path, // e.g. `Vec` in `Vec::new` or `::Item` in // `::Item::default`. - self.ty(p.span, hir::TyPath(hir::QPath::Resolved(qself, path))) + let new_id = self.next_id(); + self.ty_path(new_id, p.span, hir::QPath::Resolved(qself, path)) }; // Anything after the base path are associated "extensions", @@ -411,7 +502,7 @@ impl<'a> LoweringContext<'a> { // 3. `<>::IntoIter>::Item` // * final path is `<<>::IntoIter>::Item>::clone` for (i, segment) in p.segments.iter().enumerate().skip(proj_start) { - let segment = P(self.lower_path_segment(segment, param_mode)); + let segment = P(self.lower_path_segment(p.span, segment, param_mode, 0)); let qpath = hir::QPath::TypeRelative(ty, segment); // It's finished, return the extension of the right node type. @@ -420,7 +511,8 @@ impl<'a> LoweringContext<'a> { } // Wrap the associated extension in another type node. - ty = self.ty(p.span, hir::TyPath(qpath)); + let new_id = self.next_id(); + ty = self.ty_path(new_id, p.span, qpath); } // Should've returned in the for loop above. @@ -443,7 +535,7 @@ impl<'a> LoweringContext<'a> { hir::Path { def: self.expect_full_def(id), segments: segments.map(|segment| { - self.lower_path_segment(segment, param_mode) + self.lower_path_segment(p.span, segment, param_mode, 0) }).chain(name.map(|name| { hir::PathSegment { name: name, @@ -464,10 +556,12 @@ impl<'a> LoweringContext<'a> { } fn lower_path_segment(&mut self, + path_span: Span, segment: &PathSegment, - param_mode: ParamMode) + param_mode: ParamMode, + expected_lifetimes: usize) -> hir::PathSegment { - let parameters = if let Some(ref parameters) = segment.parameters { + let mut parameters = if let Some(ref parameters) = segment.parameters { match **parameters { PathParameters::AngleBracketed(ref data) => { let data = self.lower_angle_bracketed_parameter_data(data, param_mode); @@ -482,6 +576,14 @@ impl<'a> LoweringContext<'a> { hir::AngleBracketedParameters(data) }; + if let hir::AngleBracketedParameters(ref mut data) = parameters { + if data.lifetimes.is_empty() { + data.lifetimes = (0..expected_lifetimes).map(|_| { + self.elided_lifetime(path_span) + }).collect(); + } + } + hir::PathSegment { name: segment.identifier.name, parameters: parameters, @@ -628,10 +730,6 @@ impl<'a> LoweringContext<'a> { lts.iter().map(|l| self.lower_lifetime_def(l)).collect() } - fn lower_opt_lifetime(&mut self, o_lt: &Option) -> Option { - o_lt.as_ref().map(|lt| self.lower_lifetime(lt)) - } - fn lower_generics(&mut self, g: &Generics) -> hir::Generics { // Collect `?Trait` bounds in where clause and move them to parameter definitions. let mut add_bounds = NodeMap(); @@ -751,8 +849,12 @@ impl<'a> LoweringContext<'a> { } fn lower_trait_ref(&mut self, p: &TraitRef) -> hir::TraitRef { + let path = match self.lower_qpath(p.ref_id, &None, &p.path, ParamMode::Explicit) { + hir::QPath::Resolved(None, path) => path.and_then(|path| path), + qpath => bug!("lower_trait_ref: unexpected QPath `{:?}`", qpath) + }; hir::TraitRef { - path: self.lower_path(p.ref_id, &p.path, ParamMode::Explicit, false), + path: path, ref_id: p.ref_id, } } @@ -2269,11 +2371,40 @@ impl<'a> LoweringContext<'a> { self.expr_block(block, attrs) } - fn ty(&mut self, span: Span, node: hir::Ty_) -> P { - P(hir::Ty { + fn ty_path(&mut self, id: NodeId, span: Span, qpath: hir::QPath) -> P { + let mut id = id; + let node = match qpath { + hir::QPath::Resolved(None, path) => { + // Turn trait object paths into `TyTraitObject` instead. + if let Def::Trait(_) = path.def { + let principal = hir::PolyTraitRef { + bound_lifetimes: hir_vec![], + trait_ref: hir::TraitRef { + path: path.and_then(|path| path), + ref_id: id, + }, + span, + }; + + // The original ID is taken by the `PolyTraitRef`, + // so the `Ty` itself needs a different one. + id = self.next_id(); + + hir::TyTraitObject(hir_vec![principal], self.elided_lifetime(span)) + } else { + hir::TyPath(hir::QPath::Resolved(None, path)) + } + } + _ => hir::TyPath(qpath) + }; + P(hir::Ty { id, node, span }) + } + + fn elided_lifetime(&mut self, span: Span) -> hir::Lifetime { + hir::Lifetime { id: self.next_id(), - node: node, span: span, - }) + name: keywords::Invalid.name() + } } } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index fe086347884b0..4ebe416e1bfe6 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -77,6 +77,13 @@ pub mod svh; pub struct Lifetime { pub id: NodeId, pub span: Span, + + /// Either "'a", referring to a named lifetime definition, + /// or "" (aka keywords::Invalid), for elision placeholders. + /// + /// HIR lowering inserts these placeholders in type paths that + /// refer to type definitions needing lifetime parameters, + /// `&T` and `&mut T`, and trait objects without `... + 'a`. pub name: Name, } @@ -89,6 +96,12 @@ impl fmt::Debug for Lifetime { } } +impl Lifetime { + pub fn is_elided(&self) -> bool { + self.name == keywords::Invalid.name() + } +} + /// A lifetime definition, eg `'a: 'b+'c+'d` #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct LifetimeDef { @@ -165,30 +178,6 @@ impl PathParameters { }) } - pub fn is_empty(&self) -> bool { - match *self { - AngleBracketedParameters(ref data) => data.is_empty(), - - // Even if the user supplied no types, something like - // `X()` is equivalent to `X<(),()>`. - ParenthesizedParameters(..) => false, - } - } - - pub fn has_lifetimes(&self) -> bool { - match *self { - AngleBracketedParameters(ref data) => !data.lifetimes.is_empty(), - ParenthesizedParameters(_) => false, - } - } - - pub fn has_types(&self) -> bool { - match *self { - AngleBracketedParameters(ref data) => !data.types.is_empty(), - ParenthesizedParameters(..) => true, - } - } - /// Returns the types that the user wrote. Note that these do not necessarily map to the type /// parameters in the parenthesized case. pub fn types(&self) -> HirVec<&P> { @@ -245,12 +234,6 @@ pub struct AngleBracketedParameterData { pub bindings: HirVec, } -impl AngleBracketedParameterData { - fn is_empty(&self) -> bool { - self.lifetimes.is_empty() && self.types.is_empty() && self.bindings.is_empty() - } -} - /// A path like `Foo(A,B) -> C` #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct ParenthesizedParameterData { @@ -1208,7 +1191,7 @@ pub enum Ty_ { /// A raw pointer (`*const T` or `*mut T`) TyPtr(MutTy), /// A reference (`&'a T` or `&'a mut T`) - TyRptr(Option, MutTy), + TyRptr(Lifetime, MutTy), /// A bare function (e.g. `fn(usize) -> bool`) TyBareFn(P), /// The never type (`!`) @@ -1222,7 +1205,7 @@ pub enum Ty_ { TyPath(QPath), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. - TyTraitObject(TyParamBounds), + TyTraitObject(HirVec, Lifetime), /// An `impl Bound1 + Bound2 + Bound3` type /// where `Bound` is a trait or a lifetime. TyImplTrait(TyParamBounds), diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index d4bb2d37091b2..e058c48c59149 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -26,6 +26,7 @@ use syntax_pos::{self, BytePos}; use hir; use hir::{PatKind, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier, RangeEnd}; +use std::cell::Cell; use std::io::{self, Write, Read}; pub enum AnnNode<'a> { @@ -359,9 +360,9 @@ impl<'a> State<'a> { Ok(()) } - pub fn print_opt_lifetime(&mut self, lifetime: &Option) -> io::Result<()> { - if let Some(l) = *lifetime { - self.print_lifetime(&l)?; + pub fn print_opt_lifetime(&mut self, lifetime: &hir::Lifetime) -> io::Result<()> { + if !lifetime.is_elided() { + self.print_lifetime(lifetime)?; self.nbsp()?; } Ok(()) @@ -415,8 +416,21 @@ impl<'a> State<'a> { hir::TyPath(ref qpath) => { self.print_qpath(qpath, false)? } - hir::TyTraitObject(ref bounds) => { - self.print_bounds("", &bounds[..])?; + hir::TyTraitObject(ref bounds, ref lifetime) => { + let mut first = true; + for bound in bounds { + self.nbsp()?; + if first { + first = false; + } else { + self.word_space("+")?; + } + self.print_poly_trait_ref(bound)?; + } + if !lifetime.is_elided() { + self.word_space("+")?; + self.print_lifetime(lifetime)?; + } } hir::TyImplTrait(ref bounds) => { self.print_bounds("impl ", &bounds[..])?; @@ -1553,65 +1567,49 @@ impl<'a> State<'a> { parameters: &hir::PathParameters, colons_before_params: bool) -> io::Result<()> { - if parameters.is_empty() { - let infer_types = match *parameters { - hir::AngleBracketedParameters(ref data) => data.infer_types, - hir::ParenthesizedParameters(_) => false - }; - - // FIXME(eddyb) See the comment below about infer_types. - if !(infer_types && false) { - return Ok(()); - } - } - - if colons_before_params { - word(&mut self.s, "::")? - } - match *parameters { hir::AngleBracketedParameters(ref data) => { - word(&mut self.s, "<")?; + let start = if colons_before_params { "::<" } else { "<" }; + let empty = Cell::new(true); + let start_or_comma = |this: &mut Self| { + if empty.get() { + empty.set(false); + word(&mut this.s, start) + } else { + this.word_space(",") + } + }; - let mut comma = false; - for lifetime in &data.lifetimes { - if comma { - self.word_space(",")? + if !data.lifetimes.iter().all(|lt| lt.is_elided()) { + for lifetime in &data.lifetimes { + start_or_comma(self)?; + self.print_lifetime(lifetime)?; } - self.print_lifetime(lifetime)?; - comma = true; } if !data.types.is_empty() { - if comma { - self.word_space(",")? - } + start_or_comma(self)?; self.commasep(Inconsistent, &data.types, |s, ty| s.print_type(&ty))?; - comma = true; } // FIXME(eddyb) This would leak into error messages, e.g.: // "non-exhaustive patterns: `Some::<..>(_)` not covered". if data.infer_types && false { - if comma { - self.word_space(",")? - } + start_or_comma(self)?; word(&mut self.s, "..")?; - comma = true; } for binding in data.bindings.iter() { - if comma { - self.word_space(",")? - } + start_or_comma(self)?; self.print_name(binding.name)?; space(&mut self.s)?; self.word_space("=")?; self.print_type(&binding.ty)?; - comma = true; } - word(&mut self.s, ">")? + if !empty.get() { + word(&mut self.s, ">")? + } } hir::ParenthesizedParameters(ref data) => { diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 619a3e995c3a5..0ff9626ae11a8 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -29,7 +29,9 @@ #![feature(conservative_impl_trait)] #![feature(const_fn)] #![feature(core_intrinsics)] +#![feature(field_init_shorthand)] #![feature(libc)] +#![feature(loop_break_value)] #![feature(nonzero)] #![feature(pub_restricted)] #![feature(quote)] diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 496a3d4a49847..d11e6e3fc72bd 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -28,6 +28,7 @@ use hir::map as hir_map; use hir::map::definitions::{Definitions, DefKey, DisambiguatedDefPathData}; use hir::svh::Svh; use middle::lang_items; +use middle::resolve_lifetime::ObjectLifetimeDefault; use ty::{self, Ty, TyCtxt}; use mir::Mir; use session::Session; @@ -182,6 +183,9 @@ pub trait CrateStore<'tcx> { -> ty::GenericPredicates<'tcx>; fn item_generics<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> ty::Generics<'tcx>; + fn item_generics_own_param_counts(&self, def: DefId) -> (usize, usize); + fn item_generics_object_lifetime_defaults(&self, def: DefId) + -> Vec; fn item_attrs(&self, def_id: DefId) -> Vec; fn trait_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)-> ty::TraitDef; fn adt_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> &'tcx ty::AdtDef; @@ -331,6 +335,11 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { -> ty::GenericPredicates<'tcx> { bug!("item_super_predicates") } fn item_generics<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> ty::Generics<'tcx> { bug!("item_generics") } + fn item_generics_own_param_counts(&self, def: DefId) -> (usize, usize) + { bug!("item_generics_own_param_counts") } + fn item_generics_object_lifetime_defaults(&self, def: DefId) + -> Vec + { bug!("item_generics_object_lifetime_defaults") } fn item_attrs(&self, def_id: DefId) -> Vec { bug!("item_attrs") } fn trait_def<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId)-> ty::TraitDef { bug!("trait_def") } diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index dd99aea909faa..88ef2c69a04dc 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -15,9 +15,6 @@ //! used between functions, and they operate in a purely top-down //! way. Therefore we break lifetime name resolution into a separate pass. -pub use self::DefRegion::*; -use self::ScopeChain::*; - use dep_graph::DepNode; use hir::map::Map; use session::Session; @@ -25,45 +22,149 @@ use hir::def::Def; use hir::def_id::DefId; use middle::region; use ty; + +use std::cell::Cell; use std::mem::replace; use syntax::ast; +use syntax::attr; +use syntax::ptr::P; use syntax::symbol::keywords; use syntax_pos::Span; -use util::nodemap::NodeMap; +use errors::DiagnosticBuilder; +use util::nodemap::{NodeMap, FxHashSet, FxHashMap, DefIdMap}; +use rustc_back::slice; -use rustc_data_structures::fx::FxHashSet; use hir; -use hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap}; +use hir::intravisit::{self, Visitor, NestedVisitorMap}; #[derive(Clone, Copy, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Debug)] -pub enum DefRegion { - DefStaticRegion, - DefEarlyBoundRegion(/* index */ u32, - /* lifetime decl */ ast::NodeId), - DefLateBoundRegion(ty::DebruijnIndex, - /* lifetime decl */ ast::NodeId), - DefFreeRegion(region::CallSiteScopeData, - /* lifetime decl */ ast::NodeId), +pub enum Region { + Static, + EarlyBound(/* index */ u32, /* lifetime decl */ ast::NodeId), + LateBound(ty::DebruijnIndex, /* lifetime decl */ ast::NodeId), + LateBoundAnon(ty::DebruijnIndex, /* anon index */ u32), + Free(region::CallSiteScopeData, /* lifetime decl */ ast::NodeId), } +impl Region { + fn early(index: &mut u32, def: &hir::LifetimeDef) -> (ast::Name, Region) { + let i = *index; + *index += 1; + (def.lifetime.name, Region::EarlyBound(i, def.lifetime.id)) + } + + fn late(def: &hir::LifetimeDef) -> (ast::Name, Region) { + let depth = ty::DebruijnIndex::new(1); + (def.lifetime.name, Region::LateBound(depth, def.lifetime.id)) + } + + fn late_anon(index: &Cell) -> Region { + let i = index.get(); + index.set(i + 1); + let depth = ty::DebruijnIndex::new(1); + Region::LateBoundAnon(depth, i) + } + + fn id(&self) -> Option { + match *self { + Region::Static | + Region::LateBoundAnon(..) => None, + + 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::LateBoundAnon(depth, index) => { + Region::LateBoundAnon(depth.shifted(amount), index) + } + _ => self + } + } + + fn from_depth(self, depth: u32) -> Region { + match self { + Region::LateBound(debruijn, id) => { + Region::LateBound(ty::DebruijnIndex { + depth: debruijn.depth - (depth - 1) + }, id) + } + Region::LateBoundAnon(debruijn, index) => { + Region::LateBoundAnon(ty::DebruijnIndex { + depth: debruijn.depth - (depth - 1) + }, index) + } + _ => self + } + } + + fn subst(self, params: &[hir::Lifetime], map: &NamedRegionMap) + -> Option { + if let Region::EarlyBound(index, _) = self { + params.get(index as usize).and_then(|lifetime| { + map.defs.get(&lifetime.id).cloned() + }) + } else { + Some(self) + } + } +} + +/// A set containing, at most, one known element. +/// If two distinct values are inserted into a set, then it +/// becomes `Many`, which can be used to detect ambiguities. +#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug)] +pub enum Set1 { + Empty, + One(T), + Many +} + +impl Set1 { + pub fn insert(&mut self, value: T) { + if let Set1::Empty = *self { + *self = Set1::One(value); + return; + } + if let Set1::One(ref old) = *self { + if *old == value { + return; + } + } + *self = Set1::Many; + } +} + +pub type ObjectLifetimeDefault = Set1; + // Maps the id of each lifetime reference to the lifetime decl // that it corresponds to. pub struct NamedRegionMap { // maps from every use of a named (not anonymous) lifetime to a - // `DefRegion` describing how that region is bound - pub defs: NodeMap, + // `Region` describing how that region is bound + pub defs: NodeMap, // the set of lifetime def ids that are late-bound; late-bound ids // are named regions appearing in fn arguments that do not appear // in where-clauses pub late_bound: NodeMap, + + // For each type and trait definition, maps type parameters + // to the trait object lifetime defaults computed from them. + pub object_lifetime_defaults: NodeMap>, } struct LifetimeContext<'a, 'tcx: 'a> { sess: &'a Session, hir_map: &'a Map<'tcx>, map: &'a mut NamedRegionMap, - scope: Scope<'a>, + scope: ScopeRef<'a>, // Deep breath. Our representation for poly trait refs contains a single // binder and thus we only allow a single level of quantification. However, // the syntax of Rust permits quantification in two places, e.g., `T: for <'a> Foo<'a>` @@ -83,28 +184,75 @@ struct LifetimeContext<'a, 'tcx: 'a> { // List of labels in the function/method currently under analysis. labels_in_fn: Vec<(ast::Name, Span)>, + + // Cache for cross-crate per-definition object lifetime defaults. + xcrate_object_lifetime_defaults: DefIdMap>, +} + +#[derive(Debug)] +enum Scope<'a> { + /// Declares lifetimes, and each can be early-bound or late-bound. + /// The `DebruijnIndex` of late-bound lifetimes starts at `1` and + /// it should be shifted by the number of `Binder`s in between the + /// declaration `Binder` and the location it's referenced from. + Binder { + lifetimes: FxHashMap, + s: ScopeRef<'a> + }, + + /// Lifetimes introduced by a fn are scoped to the call-site for that fn, + /// if this is a fn body, otherwise the original definitions are used. + /// Unspecified lifetimes are inferred, unless an elision scope is nested, + /// e.g. `(&T, fn(&T) -> &T);` becomes `(&'_ T, for<'a> fn(&'a T) -> &'a T)`. + Body { + id: hir::BodyId, + s: ScopeRef<'a> + }, + + /// A scope which either determines unspecified lifetimes or errors + /// on them (e.g. due to ambiguity). For more details, see `Elide`. + Elision { + elide: Elide, + s: ScopeRef<'a> + }, + + /// Use a specific lifetime (if `Some`) or leave it unset (to be + /// inferred in a function body or potentially error outside one), + /// for the default choice of lifetime in a trait object type. + ObjectLifetimeDefault { + lifetime: Option, + s: ScopeRef<'a> + }, + + Root +} + +#[derive(Clone, Debug)] +enum Elide { + /// Use a fresh anonymous late-bound lifetime each time, by + /// incrementing the counter to generate sequential indices. + FreshLateAnon(Cell), + /// Always use this one lifetime. + Exact(Region), + /// Like `Exact(Static)` but requires `#![feature(static_in_const)]`. + Static, + /// Less or more than one lifetime were found, error on unspecified. + Error(Vec) } -#[derive(PartialEq, Debug)] -enum ScopeChain<'a> { - /// EarlyScope(['a, 'b, ...], start, s) extends s with early-bound - /// lifetimes, with consecutive parameter indices from `start`. - /// That is, 'a has index `start`, 'b has index `start + 1`, etc. - /// Indices before `start` correspond to other generic parameters - /// of a parent item (trait/impl of a method), or `Self` in traits. - EarlyScope(&'a [hir::LifetimeDef], u32, Scope<'a>), - /// LateScope(['a, 'b, ...], s) extends s with late-bound - /// lifetimes introduced by the declaration binder_id. - LateScope(&'a [hir::LifetimeDef], Scope<'a>), - - /// lifetimes introduced by a fn are scoped to the call-site for that fn. - FnScope { fn_id: ast::NodeId, body_id: ast::NodeId, s: Scope<'a> }, - RootScope +#[derive(Clone, Debug)] +struct ElisionFailureInfo { + /// Where we can find the argument pattern. + parent: Option, + /// The index of the argument in the original definition. + index: usize, + lifetime_count: usize, + have_bound_regions: bool } -type Scope<'a> = &'a ScopeChain<'a>; +type ScopeRef<'a> = &'a Scope<'a>; -static ROOT_SCOPE: ScopeChain<'static> = RootScope; +const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root; pub fn krate(sess: &Session, hir_map: &Map) @@ -114,118 +262,104 @@ pub fn krate(sess: &Session, let mut map = NamedRegionMap { defs: NodeMap(), late_bound: NodeMap(), + object_lifetime_defaults: compute_object_lifetime_defaults(sess, hir_map), }; sess.track_errors(|| { - intravisit::walk_crate(&mut LifetimeContext { + let mut visitor = LifetimeContext { sess: sess, hir_map: hir_map, map: &mut map, - scope: &ROOT_SCOPE, + scope: ROOT_SCOPE, trait_ref_hack: false, labels_in_fn: vec![], - }, krate); + xcrate_object_lifetime_defaults: DefIdMap(), + }; + for (_, item) in &krate.items { + visitor.visit_item(item); + } })?; Ok(map) } impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { - // Override the nested functions -- lifetimes follow lexical scope, - // so it's convenient to walk the tree in lexical order. fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> { - NestedVisitorMap::All(&self.hir_map) + NestedVisitorMap::All(self.hir_map) + } + + // We want to nest trait/impl items in their parent, but nothing else. + fn visit_nested_item(&mut self, _: hir::ItemId) {} + + fn visit_nested_body(&mut self, body: hir::BodyId) { + // Each body has their own set of labels, save labels. + let saved = replace(&mut self.labels_in_fn, vec![]); + let body = self.hir_map.body(body); + extract_labels(self, body); + self.with(Scope::Body { id: body.id(), s: self.scope }, |_, this| { + this.visit_body(body); + }); + replace(&mut self.labels_in_fn, saved); } fn visit_item(&mut self, item: &'tcx hir::Item) { - // Save labels for nested items. - let saved_labels_in_fn = replace(&mut self.labels_in_fn, vec![]); - - // Items always introduce a new root scope - self.with(RootScope, |_, this| { - match item.node { - hir::ItemFn(..) => { - // Fn lifetimes get added in visit_fn below: + match item.node { + hir::ItemFn(ref decl, _, _, _, ref generics, _) => { + self.visit_early_late(item.id, None, decl, generics, |this| { intravisit::walk_item(this, item); - } - hir::ItemExternCrate(_) | - hir::ItemUse(..) | - hir::ItemMod(..) | - hir::ItemDefaultImpl(..) | - hir::ItemForeignMod(..) | - hir::ItemStatic(..) | - hir::ItemConst(..) => { - // These sorts of items have no lifetime parameters at all. + }); + } + hir::ItemExternCrate(_) | + hir::ItemUse(..) | + hir::ItemMod(..) | + hir::ItemDefaultImpl(..) | + hir::ItemForeignMod(..) => { + // These sorts of items have no lifetime parameters at all. + intravisit::walk_item(self, item); + } + hir::ItemStatic(..) | + hir::ItemConst(..) => { + // No lifetime parameters, but implied 'static. + let scope = Scope::Elision { + elide: Elide::Static, + s: ROOT_SCOPE + }; + self.with(scope, |_, this| intravisit::walk_item(this, item)); + } + hir::ItemTy(_, ref generics) | + hir::ItemEnum(_, ref generics) | + hir::ItemStruct(_, ref generics) | + hir::ItemUnion(_, ref generics) | + hir::ItemTrait(_, ref generics, ..) | + hir::ItemImpl(_, _, ref generics, ..) => { + // These kinds of items have only early bound lifetime parameters. + let mut index = if let hir::ItemTrait(..) = item.node { + 1 // Self comes before lifetimes + } else { + 0 + }; + let lifetimes = generics.lifetimes.iter().map(|def| { + Region::early(&mut index, def) + }).collect(); + let scope = Scope::Binder { + lifetimes: lifetimes, + s: ROOT_SCOPE + }; + self.with(scope, |old_scope, this| { + this.check_lifetime_defs(old_scope, &generics.lifetimes); intravisit::walk_item(this, item); - } - hir::ItemTy(_, ref generics) | - hir::ItemEnum(_, ref generics) | - hir::ItemStruct(_, ref generics) | - hir::ItemUnion(_, ref generics) | - hir::ItemTrait(_, ref generics, ..) | - hir::ItemImpl(_, _, ref generics, ..) => { - // These kinds of items have only early bound lifetime parameters. - let lifetimes = &generics.lifetimes; - let start = if let hir::ItemTrait(..) = item.node { - 1 // Self comes before lifetimes - } else { - 0 - }; - this.with(EarlyScope(lifetimes, start, &ROOT_SCOPE), |old_scope, this| { - this.check_lifetime_defs(old_scope, lifetimes); - intravisit::walk_item(this, item); - }); - } + }); } - }); - - // Done traversing the item; remove any labels it created - self.labels_in_fn = saved_labels_in_fn; + } } fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem) { - // Items save/restore the set of labels. This way inner items - // can freely reuse names, be they loop labels or lifetimes. - let saved = replace(&mut self.labels_in_fn, vec![]); - - // Items always introduce a new root scope - self.with(RootScope, |_, this| { - match item.node { - hir::ForeignItemFn(ref decl, _, ref generics) => { - this.visit_early_late(item.id, decl, generics, |this| { - intravisit::walk_foreign_item(this, item); - }) - } - hir::ForeignItemStatic(..) => { + match item.node { + hir::ForeignItemFn(ref decl, _, ref generics) => { + self.visit_early_late(item.id, None, decl, generics, |this| { intravisit::walk_foreign_item(this, item); - } - } - }); - - // Done traversing the item; restore saved set of labels. - replace(&mut self.labels_in_fn, saved); - } - - fn visit_fn(&mut self, fk: FnKind<'tcx>, decl: &'tcx hir::FnDecl, - b: hir::BodyId, s: Span, fn_id: ast::NodeId) { - match fk { - FnKind::ItemFn(_, generics, ..) => { - self.visit_early_late(fn_id,decl, generics, |this| { - this.add_scope_and_walk_fn(fk, decl, b, s, fn_id) }) } - FnKind::Method(_, sig, ..) => { - self.visit_early_late( - fn_id, - decl, - &sig.generics, - |this| this.add_scope_and_walk_fn(fk, decl, b, s, fn_id)); - } - FnKind::Closure(_) => { - // Closures have their own set of labels, save labels just - // like for foreign items above. - let saved = replace(&mut self.labels_in_fn, vec![]); - let result = self.add_scope_and_walk_fn(fk, decl, b, s, fn_id); - replace(&mut self.labels_in_fn, saved); - result + hir::ForeignItemStatic(..) => { + intravisit::walk_foreign_item(self, item); } } } @@ -233,27 +367,35 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn visit_ty(&mut self, ty: &'tcx hir::Ty) { match ty.node { hir::TyBareFn(ref c) => { - self.with(LateScope(&c.lifetimes, self.scope), |old_scope, this| { + let scope = Scope::Binder { + lifetimes: c.lifetimes.iter().map(Region::late).collect(), + s: self.scope + }; + self.with(scope, |old_scope, this| { // a bare fn has no bounds, so everything // contained within is scoped within its binder. this.check_lifetime_defs(old_scope, &c.lifetimes); intravisit::walk_ty(this, ty); }); } - hir::TyPath(hir::QPath::Resolved(None, ref path)) => { - // if this path references a trait, then this will resolve to - // a trait ref, which introduces a binding scope. - match path.def { - Def::Trait(..) => { - self.with(LateScope(&[], self.scope), |_, this| { - this.visit_path(path, ty.id); - }); - } - _ => { - intravisit::walk_ty(self, ty); - } + hir::TyTraitObject(ref bounds, ref lifetime) => { + for bound in bounds { + self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); + } + if lifetime.is_elided() { + self.resolve_object_lifetime_default(lifetime) + } else { + self.visit_lifetime(lifetime); } } + hir::TyRptr(ref lifetime_ref, ref mt) => { + self.visit_lifetime(lifetime_ref); + let scope = Scope::ObjectLifetimeDefault { + lifetime: self.map.defs.get(&lifetime_ref.id).cloned(), + s: self.scope + }; + self.with(scope, |_, this| this.visit_ty(&mt.ty)); + } _ => { intravisit::walk_ty(self, ty) } @@ -261,31 +403,56 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { } fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) { - // We reset the labels on every trait item, so that different - // methods in an impl can reuse label names. - let saved = replace(&mut self.labels_in_fn, vec![]); - - if let hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Required(_)) = - trait_item.node { + if let hir::TraitItemKind::Method(ref sig, _) = trait_item.node { self.visit_early_late( trait_item.id, + Some(self.hir_map.get_parent(trait_item.id)), &sig.decl, &sig.generics, |this| intravisit::walk_trait_item(this, trait_item)) } else { intravisit::walk_trait_item(self, trait_item); } + } - replace(&mut self.labels_in_fn, saved); + fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) { + if let hir::ImplItemKind::Method(ref sig, _) = impl_item.node { + self.visit_early_late( + impl_item.id, + Some(self.hir_map.get_parent(impl_item.id)), + &sig.decl, &sig.generics, + |this| intravisit::walk_impl_item(this, impl_item)) + } else { + intravisit::walk_impl_item(self, impl_item); + } } fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) { + if lifetime_ref.is_elided() { + self.resolve_elided_lifetimes(slice::ref_slice(lifetime_ref)); + return; + } if lifetime_ref.name == keywords::StaticLifetime.name() { - self.insert_lifetime(lifetime_ref, DefStaticRegion); + self.insert_lifetime(lifetime_ref, Region::Static); return; } self.resolve_lifetime_ref(lifetime_ref); } + fn visit_path(&mut self, path: &'tcx hir::Path, _: ast::NodeId) { + for (i, segment) in path.segments.iter().enumerate() { + let depth = path.segments.len() - i - 1; + self.visit_segment_parameters(path.def, depth, &segment.parameters); + } + } + + fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl) { + let output = match fd.output { + hir::DefaultReturn(_) => None, + hir::Return(ref ty) => Some(ty) + }; + self.visit_fn_like_elision(&fd.inputs, output); + } + fn visit_generics(&mut self, generics: &'tcx hir::Generics) { for ty_param in generics.ty_params.iter() { walk_list!(self, visit_ty_param_bound, &ty_param.bounds); @@ -301,8 +468,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { .. }) => { if !bound_lifetimes.is_empty() { self.trait_ref_hack = true; - let result = self.with(LateScope(bound_lifetimes, self.scope), - |old_scope, this| { + let scope = Scope::Binder { + lifetimes: bound_lifetimes.iter().map(Region::late).collect(), + s: self.scope + }; + let result = self.with(scope, |old_scope, this| { this.check_lifetime_defs(old_scope, bound_lifetimes); this.visit_ty(&bounded_ty); walk_list!(this, visit_ty_param_bound, bounds); @@ -335,7 +505,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { fn visit_poly_trait_ref(&mut self, trait_ref: &'tcx hir::PolyTraitRef, - _modifier: &'tcx hir::TraitBoundModifier) { + _modifier: hir::TraitBoundModifier) { debug!("visit_poly_trait_ref trait_ref={:?}", trait_ref); if !self.trait_ref_hack || !trait_ref.bound_lifetimes.is_empty() { @@ -343,12 +513,16 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { span_err!(self.sess, trait_ref.span, E0316, "nested quantification of lifetimes"); } - self.with(LateScope(&trait_ref.bound_lifetimes, self.scope), |old_scope, this| { + let scope = Scope::Binder { + lifetimes: trait_ref.bound_lifetimes.iter().map(Region::late).collect(), + s: self.scope + }; + self.with(scope, |old_scope, this| { this.check_lifetime_defs(old_scope, &trait_ref.bound_lifetimes); for lifetime in &trait_ref.bound_lifetimes { this.visit_lifetime_def(lifetime); } - intravisit::walk_path(this, &trait_ref.trait_ref.path) + this.visit_trait_ref(&trait_ref.trait_ref) }) } else { self.visit_trait_ref(&trait_ref.trait_ref) @@ -367,8 +541,8 @@ fn original_label(span: Span) -> Original { fn shadower_label(span: Span) -> Shadower { Shadower { kind: ShadowKind::Label, span: span } } -fn original_lifetime(l: &hir::Lifetime) -> Original { - Original { kind: ShadowKind::Lifetime, span: l.span } +fn original_lifetime(span: Span) -> Original { + Original { kind: ShadowKind::Lifetime, span: span } } fn shadower_lifetime(l: &hir::Lifetime) -> Shadower { Shadower { kind: ShadowKind::Lifetime, span: l.span } @@ -406,33 +580,28 @@ fn signal_shadowing_problem(sess: &Session, name: ast::Name, orig: Original, sha // Adds all labels in `b` to `ctxt.labels_in_fn`, signalling a warning // if one of the label shadows a lifetime or another label. -fn extract_labels(ctxt: &mut LifetimeContext, b: hir::BodyId) { - struct GatherLabels<'a> { +fn extract_labels(ctxt: &mut LifetimeContext, body: &hir::Body) { + struct GatherLabels<'a, 'tcx: 'a> { sess: &'a Session, - scope: Scope<'a>, + hir_map: &'a Map<'tcx>, + scope: ScopeRef<'a>, labels_in_fn: &'a mut Vec<(ast::Name, Span)>, } let mut gather = GatherLabels { sess: ctxt.sess, + hir_map: ctxt.hir_map, scope: ctxt.scope, labels_in_fn: &mut ctxt.labels_in_fn, }; - gather.visit_body(ctxt.hir_map.body(b)); - return; + gather.visit_body(body); - impl<'v, 'a> Visitor<'v> for GatherLabels<'a> { + impl<'v, 'a, 'tcx> Visitor<'v> for GatherLabels<'a, 'tcx> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> { NestedVisitorMap::None } - fn visit_expr(&mut self, ex: &'v hir::Expr) { - // do not recurse into closures defined in the block - // since they are treated as separate fns from the POV of - // labels_in_fn - if let hir::ExprClosure(..) = ex.node { - return - } + fn visit_expr(&mut self, ex: &hir::Expr) { if let Some((label, label_span)) = expression_label(ex) { for &(prior, prior_span) in &self.labels_in_fn[..] { // FIXME (#24278): non-hygienic comparison @@ -445,6 +614,7 @@ fn extract_labels(ctxt: &mut LifetimeContext, b: hir::BodyId) { } check_if_label_shadows_lifetime(self.sess, + self.hir_map, self.scope, label, label_span); @@ -453,10 +623,6 @@ fn extract_labels(ctxt: &mut LifetimeContext, b: hir::BodyId) { } intravisit::walk_expr(self, ex) } - - fn visit_item(&mut self, _: &hir::Item) { - // do not recurse into items defined in the block - } } fn expression_label(ex: &hir::Expr) -> Option<(ast::Name, Span)> { @@ -468,26 +634,27 @@ fn extract_labels(ctxt: &mut LifetimeContext, b: hir::BodyId) { } fn check_if_label_shadows_lifetime<'a>(sess: &'a Session, - mut scope: Scope<'a>, + hir_map: &Map, + mut scope: ScopeRef<'a>, label: ast::Name, label_span: Span) { loop { match *scope { - FnScope { s, .. } => { scope = s; } - RootScope => { return; } - - EarlyScope(lifetimes, _, s) | - LateScope(lifetimes, s) => { - for lifetime_def in lifetimes { - // FIXME (#24278): non-hygienic comparison - if label == lifetime_def.lifetime.name { - signal_shadowing_problem( - sess, - label, - original_lifetime(&lifetime_def.lifetime), - shadower_label(label_span)); - return; - } + Scope::Body { s, .. } | + Scope::Elision { s, .. } | + Scope::ObjectLifetimeDefault { s, .. } => { scope = s; } + + Scope::Root => { return; } + + Scope::Binder { ref lifetimes, s } => { + // FIXME (#24278): non-hygienic comparison + if let Some(def) = lifetimes.get(&label) { + signal_shadowing_problem( + sess, + label, + original_lifetime(hir_map.span(def.id().unwrap())), + shadower_label(label_span)); + return; } scope = s; } @@ -496,35 +663,104 @@ fn extract_labels(ctxt: &mut LifetimeContext, b: hir::BodyId) { } } -impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { - fn add_scope_and_walk_fn(&mut self, - fk: FnKind<'tcx>, - fd: &'tcx hir::FnDecl, - fb: hir::BodyId, - _span: Span, - fn_id: ast::NodeId) { - match fk { - FnKind::ItemFn(_, generics, ..) => { - intravisit::walk_fn_decl(self, fd); - self.visit_generics(generics); - } - FnKind::Method(_, sig, ..) => { - intravisit::walk_fn_decl(self, fd); - self.visit_generics(&sig.generics); +fn compute_object_lifetime_defaults(sess: &Session, hir_map: &Map) + -> NodeMap> { + let mut map = NodeMap(); + for item in hir_map.krate().items.values() { + match item.node { + hir::ItemStruct(_, ref generics) | + hir::ItemUnion(_, ref generics) | + hir::ItemEnum(_, ref generics) | + hir::ItemTy(_, ref generics) | + hir::ItemTrait(_, ref generics, ..) => { + let result = object_lifetime_defaults_for_item(hir_map, generics); + + // Debugging aid. + if attr::contains_name(&item.attrs, "rustc_object_lifetime_default") { + let object_lifetime_default_reprs: String = + result.iter().map(|set| { + match *set { + Set1::Empty => "BaseDefault".to_string(), + Set1::One(Region::Static) => "'static".to_string(), + Set1::One(Region::EarlyBound(i, _)) => { + generics.lifetimes[i as usize].lifetime.name.to_string() + } + Set1::One(_) => bug!(), + Set1::Many => "Ambiguous".to_string(), + } + }).collect::>().join(","); + sess.span_err(item.span, &object_lifetime_default_reprs); + } + + map.insert(item.id, result); } - FnKind::Closure(_) => { - intravisit::walk_fn_decl(self, fd); + _ => {} + } + } + map +} + +/// Scan the bounds and where-clauses on parameters to extract bounds +/// of the form `T:'a` so as to determine the `ObjectLifetimeDefault` +/// for each type parameter. +fn object_lifetime_defaults_for_item(hir_map: &Map, generics: &hir::Generics) + -> Vec { + fn add_bounds(set: &mut Set1, bounds: &[hir::TyParamBound]) { + for bound in bounds { + if let hir::RegionTyParamBound(ref lifetime) = *bound { + set.insert(lifetime.name); } } + } - // After inpsecting the decl, add all labels from the body to - // `self.labels_in_fn`. - extract_labels(self, fb); + generics.ty_params.iter().map(|param| { + let mut set = Set1::Empty; - self.with(FnScope { fn_id: fn_id, body_id: fb.node_id, s: self.scope }, - |_old_scope, this| this.visit_nested_body(fb)) - } + add_bounds(&mut set, ¶m.bounds); + + let param_def_id = hir_map.local_def_id(param.id); + for predicate in &generics.where_clause.predicates { + // Look for `type: ...` where clauses. + let data = match *predicate { + hir::WherePredicate::BoundPredicate(ref data) => data, + _ => continue + }; + + // Ignore `for<'a> type: ...` as they can change what + // lifetimes mean (although we could "just" handle it). + if !data.bound_lifetimes.is_empty() { + continue; + } + + let def = match data.bounded_ty.node { + hir::TyPath(hir::QPath::Resolved(None, ref path)) => path.def, + _ => continue + }; + if def == Def::TyParam(param_def_id) { + add_bounds(&mut set, &data.bounds); + } + } + + match set { + Set1::Empty => Set1::Empty, + Set1::One(name) => { + if name == keywords::StaticLifetime.name() { + Set1::One(Region::Static) + } else { + generics.lifetimes.iter().enumerate().find(|&(_, def)| { + def.lifetime.name == name + }).map_or(Set1::Many, |(i, def)| { + Set1::One(Region::EarlyBound(i as u32, def.lifetime.id)) + }) + } + } + Set1::Many => Set1::Many + } + }).collect() +} + +impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { // FIXME(#37666) this works around a limitation in the region inferencer fn hack(&mut self, f: F) where F: for<'b> FnOnce(&mut LifetimeContext<'b, 'tcx>), @@ -532,21 +768,27 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { f(self) } - fn with(&mut self, wrap_scope: ScopeChain, f: F) where - F: for<'b> FnOnce(Scope, &mut LifetimeContext<'b, 'tcx>), + fn with(&mut self, wrap_scope: Scope, f: F) where + F: for<'b> FnOnce(ScopeRef, &mut LifetimeContext<'b, 'tcx>), { let LifetimeContext {sess, hir_map, ref mut map, ..} = *self; + let labels_in_fn = replace(&mut self.labels_in_fn, vec![]); + let xcrate_object_lifetime_defaults = + replace(&mut self.xcrate_object_lifetime_defaults, DefIdMap()); let mut this = LifetimeContext { sess: sess, hir_map: hir_map, map: *map, scope: &wrap_scope, trait_ref_hack: self.trait_ref_hack, - labels_in_fn: self.labels_in_fn.clone(), + labels_in_fn: labels_in_fn, + xcrate_object_lifetime_defaults: xcrate_object_lifetime_defaults, }; debug!("entering scope {:?}", this.scope); f(self.scope, &mut this); debug!("exiting scope {:?}", this.scope); + self.labels_in_fn = this.labels_in_fn; + self.xcrate_object_lifetime_defaults = this.xcrate_object_lifetime_defaults; } /// Visits self by adding a scope and handling recursive walk over the contents with `walk`. @@ -569,6 +811,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { /// ordering is not important there. fn visit_early_late(&mut self, fn_id: ast::NodeId, + parent_id: Option, decl: &'tcx hir::FnDecl, generics: &'tcx hir::Generics, walk: F) where @@ -580,156 +823,618 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { decl, generics); - let (late, early): (Vec<_>, _) = - generics.lifetimes - .iter() - .cloned() - .partition(|l| self.map.late_bound.contains_key(&l.lifetime.id)); - // Find the start of nested early scopes, e.g. in methods. - let mut start = 0; - if let EarlyScope(..) = *self.scope { - let parent = self.hir_map.expect_item(self.hir_map.get_parent(fn_id)); + let mut index = 0; + if let Some(parent_id) = parent_id { + let parent = self.hir_map.expect_item(parent_id); if let hir::ItemTrait(..) = parent.node { - start += 1; // Self comes first. + index += 1; // Self comes first. } match parent.node { hir::ItemTrait(_, ref generics, ..) | hir::ItemImpl(_, _, ref generics, ..) => { - start += generics.lifetimes.len() + generics.ty_params.len(); + index += (generics.lifetimes.len() + generics.ty_params.len()) as u32; } _ => {} } } - self.with(EarlyScope(&early, start as u32, self.scope), move |old_scope, this| { - this.with(LateScope(&late, this.scope), move |_, this| { - this.check_lifetime_defs(old_scope, &generics.lifetimes); - this.hack(walk); // FIXME(#37666) workaround in place of `walk(this)` - }); + let lifetimes = generics.lifetimes.iter().map(|def| { + if self.map.late_bound.contains_key(&def.lifetime.id) { + Region::late(def) + } else { + Region::early(&mut index, def) + } + }).collect(); + + let scope = Scope::Binder { + lifetimes: lifetimes, + s: self.scope + }; + self.with(scope, move |old_scope, this| { + this.check_lifetime_defs(old_scope, &generics.lifetimes); + this.hack(walk); // FIXME(#37666) workaround in place of `walk(this)` }); } fn resolve_lifetime_ref(&mut self, lifetime_ref: &hir::Lifetime) { // Walk up the scope chain, tracking the number of fn scopes // that we pass through, until we find a lifetime with the - // given name or we run out of scopes. If we encounter a code - // block, then the lifetime is not bound but free, so switch - // over to `resolve_free_lifetime_ref()` to complete the + // given name or we run out of scopes. // search. let mut late_depth = 0; let mut scope = self.scope; - loop { + let mut outermost_body = None; + let result = loop { match *scope { - FnScope {fn_id, body_id, s } => { - return self.resolve_free_lifetime_ref( - region::CallSiteScopeData { fn_id: fn_id, body_id: body_id }, - lifetime_ref, - s); + Scope::Body { id, s } => { + outermost_body = Some(id); + scope = s; } - RootScope => { - break; + Scope::Root => { + break None; } - EarlyScope(lifetimes, start, s) => { - match search_lifetimes(lifetimes, lifetime_ref) { - Some((index, lifetime_def)) => { - let decl_id = lifetime_def.id; - let def = DefEarlyBoundRegion(start + index, decl_id); - self.insert_lifetime(lifetime_ref, def); - return; - } - None => { + Scope::Binder { ref lifetimes, s } => { + if let Some(&def) = lifetimes.get(&lifetime_ref.name) { + break Some(def.shifted(late_depth)); + } else { + late_depth += 1; + scope = s; + } + } + + Scope::Elision { s, .. } | + Scope::ObjectLifetimeDefault { s, .. } => { + scope = s; + } + } + }; + + if let Some(mut def) = result { + if let Some(body_id) = outermost_body { + let fn_id = self.hir_map.body_owner(body_id); + let scope_data = region::CallSiteScopeData { + fn_id: fn_id, body_id: body_id.node_id + }; + match self.hir_map.get(fn_id) { + hir::map::NodeItem(&hir::Item { + node: hir::ItemFn(..), .. + }) | + hir::map::NodeTraitItem(&hir::TraitItem { + node: hir::TraitItemKind::Method(..), .. + }) | + hir::map::NodeImplItem(&hir::ImplItem { + node: hir::ImplItemKind::Method(..), .. + }) => { + def = Region::Free(scope_data, def.id().unwrap()); + } + _ => {} + } + } + self.insert_lifetime(lifetime_ref, def); + } else { + struct_span_err!(self.sess, lifetime_ref.span, E0261, + "use of undeclared lifetime name `{}`", lifetime_ref.name) + .span_label(lifetime_ref.span, &format!("undeclared lifetime")) + .emit(); + } + } + + fn visit_segment_parameters(&mut self, + def: Def, + depth: usize, + params: &'tcx hir::PathParameters) { + let data = match *params { + hir::ParenthesizedParameters(ref data) => { + self.visit_fn_like_elision(&data.inputs, data.output.as_ref()); + return; + } + hir::AngleBracketedParameters(ref data) => data + }; + + if data.lifetimes.iter().all(|l| l.is_elided()) { + self.resolve_elided_lifetimes(&data.lifetimes); + } else { + for l in &data.lifetimes { self.visit_lifetime(l); } + } + + // Figure out if this is a type/trait segment, + // which requires object lifetime defaults. + let parent_def_id = |this: &mut Self, def_id: DefId| { + let def_key = if def_id.is_local() { + this.hir_map.def_key(def_id) + } else { + this.sess.cstore.def_key(def_id) + }; + DefId { + krate: def_id.krate, + index: def_key.parent.expect("missing parent") + } + }; + let type_def_id = match def { + Def::AssociatedTy(def_id) if depth == 1 => { + Some(parent_def_id(self, def_id)) + } + Def::Variant(def_id) if depth == 0 => { + Some(parent_def_id(self, def_id)) + } + Def::Struct(def_id) | + Def::Union(def_id) | + Def::Enum(def_id) | + Def::TyAlias(def_id) | + Def::Trait(def_id) if depth == 0 => Some(def_id), + _ => None + }; + + let object_lifetime_defaults = type_def_id.map_or(vec![], |def_id| { + let in_body = { + let mut scope = self.scope; + loop { + match *scope { + Scope::Root => break false, + + Scope::Body { .. } => break true, + + Scope::Binder { s, .. } | + Scope::Elision { s, .. } | + Scope::ObjectLifetimeDefault { s, .. } => { scope = s; } } } + }; + + let map = &self.map; + let unsubst = if let Some(id) = self.hir_map.as_local_node_id(def_id) { + &map.object_lifetime_defaults[&id] + } else { + let cstore = &self.sess.cstore; + self.xcrate_object_lifetime_defaults.entry(def_id).or_insert_with(|| { + cstore.item_generics_object_lifetime_defaults(def_id) + }) + }; + unsubst.iter().map(|set| { + match *set { + Set1::Empty => { + if in_body { + None + } else { + Some(Region::Static) + } + } + Set1::One(r) => r.subst(&data.lifetimes, map), + Set1::Many => None + } + }).collect() + }); + + for (i, ty) in data.types.iter().enumerate() { + if let Some(<) = object_lifetime_defaults.get(i) { + let scope = Scope::ObjectLifetimeDefault { + lifetime: lt, + s: self.scope + }; + self.with(scope, |_, this| this.visit_ty(ty)); + } else { + self.visit_ty(ty); + } + } + + for b in &data.bindings { self.visit_assoc_type_binding(b); } + } + + fn visit_fn_like_elision(&mut self, inputs: &'tcx [P], + output: Option<&'tcx P>) { + let mut arg_elide = Elide::FreshLateAnon(Cell::new(0)); + let arg_scope = Scope::Elision { + elide: arg_elide.clone(), + s: self.scope + }; + self.with(arg_scope, |_, this| { + for input in inputs { + this.visit_ty(input); + } + match *this.scope { + Scope::Elision { ref elide, .. } => { + arg_elide = elide.clone(); + } + _ => bug!() + } + }); + + let output = match output { + Some(ty) => ty, + None => return + }; + + // Figure out if there's a body we can get argument names from, + // and whether there's a `self` argument (treated specially). + let mut assoc_item_kind = None; + let mut impl_self = None; + let parent = self.hir_map.get_parent_node(output.id); + let body = match self.hir_map.get(parent) { + // `fn` definitions and methods. + hir::map::NodeItem(&hir::Item { + node: hir::ItemFn(.., body), .. + }) => Some(body), + + hir::map::NodeTraitItem(&hir::TraitItem { + node: hir::TraitItemKind::Method(_, ref m), .. + }) => { + match self.hir_map.expect_item(self.hir_map.get_parent(parent)).node { + hir::ItemTrait(.., ref trait_items) => { + assoc_item_kind = trait_items.iter().find(|ti| ti.id.node_id == parent) + .map(|ti| ti.kind); + } + _ => {} + } + match *m { + hir::TraitMethod::Required(_) => None, + hir::TraitMethod::Provided(body) => Some(body), + } + } + + hir::map::NodeImplItem(&hir::ImplItem { + node: hir::ImplItemKind::Method(_, body), .. + }) => { + match self.hir_map.expect_item(self.hir_map.get_parent(parent)).node { + hir::ItemImpl(.., ref self_ty, ref impl_items) => { + impl_self = Some(self_ty); + assoc_item_kind = impl_items.iter().find(|ii| ii.id.node_id == parent) + .map(|ii| ii.kind); + } + _ => {} + } + Some(body) + } + + // `fn(...) -> R` and `Trait(...) -> R` (both types and bounds). + hir::map::NodeTy(_) | hir::map::NodeTraitRef(_) => None, + + // Foreign `fn` decls are terrible because we messed up, + // and their return types get argument type elision. + // And now too much code out there is abusing this rule. + hir::map::NodeForeignItem(_) => { + let arg_scope = Scope::Elision { + elide: arg_elide, + s: self.scope + }; + self.with(arg_scope, |_, this| this.visit_ty(output)); + return; + } + + // Everything else (only closures?) doesn't + // actually enjoy elision in return types. + _ => { + self.visit_ty(output); + return; + } + }; + + let has_self = match assoc_item_kind { + Some(hir::AssociatedItemKind::Method { has_self }) => has_self, + _ => false + }; + + // In accordance with the rules for lifetime elision, we can determine + // what region to use for elision in the output type in two ways. + // First (determined here), if `self` is by-reference, then the + // implied output region is the region of the self parameter. + if has_self { + // Look for `self: &'a Self` - also desugared from `&'a self`, + // and if that matches, use it for elision and return early. + let is_self_ty = |def: Def| { + if let Def::SelfTy(..) = def { + return true; + } + + // Can't always rely on literal (or implied) `Self` due + // to the way elision rules were originally specified. + let impl_self = impl_self.map(|ty| &ty.node); + if let Some(&hir::TyPath(hir::QPath::Resolved(None, ref path))) = impl_self { + match path.def { + // Whitelist the types that unambiguously always + // result in the same type constructor being used + // (it can't differ between `Self` and `self`). + Def::Struct(_) | + Def::Union(_) | + Def::Enum(_) | + Def::PrimTy(_) => return def == path.def, + _ => {} + } + } - LateScope(lifetimes, s) => { - match search_lifetimes(lifetimes, lifetime_ref) { - Some((_index, lifetime_def)) => { - let decl_id = lifetime_def.id; - let debruijn = ty::DebruijnIndex::new(late_depth + 1); - let def = DefLateBoundRegion(debruijn, decl_id); - self.insert_lifetime(lifetime_ref, def); + false + }; + + if let hir::TyRptr(lifetime_ref, ref mt) = inputs[0].node { + if let hir::TyPath(hir::QPath::Resolved(None, ref path)) = mt.ty.node { + if is_self_ty(path.def) { + if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.id) { + let scope = Scope::Elision { + elide: Elide::Exact(lifetime), + s: self.scope + }; + self.with(scope, |_, this| this.visit_ty(output)); return; } + } + } + } + } - None => { - late_depth += 1; - scope = s; + // Second, if there was exactly one lifetime (either a substitution or a + // reference) in the arguments, then any anonymous regions in the output + // have that lifetime. + let mut possible_implied_output_region = None; + let mut lifetime_count = 0; + let arg_lifetimes = inputs.iter().enumerate().skip(has_self as usize).map(|(i, input)| { + let mut gather = GatherLifetimes { + map: self.map, + binder_depth: 1, + have_bound_regions: false, + lifetimes: FxHashSet() + }; + gather.visit_ty(input); + + lifetime_count += gather.lifetimes.len(); + + if lifetime_count == 1 && gather.lifetimes.len() == 1 { + // there's a chance that the unique lifetime of this + // iteration will be the appropriate lifetime for output + // parameters, so lets store it. + possible_implied_output_region = gather.lifetimes.iter().cloned().next(); + } + + ElisionFailureInfo { + parent: body, + index: i, + lifetime_count: gather.lifetimes.len(), + have_bound_regions: gather.have_bound_regions + } + }).collect(); + + let elide = if lifetime_count == 1 { + Elide::Exact(possible_implied_output_region.unwrap()) + } else { + Elide::Error(arg_lifetimes) + }; + + let scope = Scope::Elision { + elide: elide, + s: self.scope + }; + self.with(scope, |_, this| this.visit_ty(output)); + + struct GatherLifetimes<'a> { + map: &'a NamedRegionMap, + binder_depth: u32, + have_bound_regions: bool, + lifetimes: FxHashSet, + } + + impl<'v, 'a> Visitor<'v> for GatherLifetimes<'a> { + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> { + NestedVisitorMap::None + } + + fn visit_ty(&mut self, ty: &hir::Ty) { + if let hir::TyBareFn(_) = ty.node { + self.binder_depth += 1; + } + if let hir::TyTraitObject(ref bounds, ref lifetime) = ty.node { + for bound in bounds { + self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); + } + + // Stay on the safe side and don't include the object + // lifetime default (which may not end up being used). + if !lifetime.is_elided() { + self.visit_lifetime(lifetime); + } + } else { + intravisit::walk_ty(self, ty); + } + if let hir::TyBareFn(_) = ty.node { + self.binder_depth -= 1; + } + } + + fn visit_poly_trait_ref(&mut self, + trait_ref: &hir::PolyTraitRef, + modifier: hir::TraitBoundModifier) { + self.binder_depth += 1; + intravisit::walk_poly_trait_ref(self, trait_ref, modifier); + self.binder_depth -= 1; + } + + fn visit_lifetime_def(&mut self, lifetime_def: &hir::LifetimeDef) { + for l in &lifetime_def.bounds { self.visit_lifetime(l); } + } + + 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::LateBoundAnon(debruijn, _) + if debruijn.depth < self.binder_depth => { + self.have_bound_regions = true; + } + _ => { + self.lifetimes.insert(lifetime.from_depth(self.binder_depth)); } } } } } - self.unresolved_lifetime_ref(lifetime_ref); } - fn resolve_free_lifetime_ref(&mut self, - scope_data: region::CallSiteScopeData, - lifetime_ref: &hir::Lifetime, - scope: Scope) { - debug!("resolve_free_lifetime_ref \ - scope_data: {:?} lifetime_ref: {:?} scope: {:?}", - scope_data, lifetime_ref, scope); + fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[hir::Lifetime]) { + if lifetime_refs.is_empty() { + return; + } - // Walk up the scope chain, tracking the outermost free scope, - // until we encounter a scope that contains the named lifetime - // or we run out of scopes. - let mut scope_data = scope_data; - let mut scope = scope; - let mut search_result = None; - loop { - debug!("resolve_free_lifetime_ref \ - scope_data: {:?} scope: {:?} search_result: {:?}", - scope_data, scope, search_result); + let span = lifetime_refs[0].span; + let mut late_depth = 0; + let mut scope = self.scope; + let error = loop { match *scope { - FnScope { fn_id, body_id, s } => { - scope_data = region::CallSiteScopeData { - fn_id: fn_id, body_id: body_id - }; + // Do not assign any resolution, it will be inferred. + Scope::Body { .. } => return, + + Scope::Root => break None, + + Scope::Binder { s, .. } => { + late_depth += 1; scope = s; } - RootScope => { - break; + Scope::Elision { ref elide, .. } => { + let lifetime = match *elide { + Elide::FreshLateAnon(ref counter) => { + for lifetime_ref in lifetime_refs { + let lifetime = Region::late_anon(counter).shifted(late_depth); + self.insert_lifetime(lifetime_ref, lifetime); + } + return; + } + Elide::Exact(l) => l.shifted(late_depth), + Elide::Static => { + if !self.sess.features.borrow().static_in_const { + self.sess + .struct_span_err(span, + "this needs a `'static` lifetime or the \ + `static_in_const` feature, see #35897") + .emit(); + } + Region::Static + } + Elide::Error(ref e) => break Some(e) + }; + for lifetime_ref in lifetime_refs { + self.insert_lifetime(lifetime_ref, lifetime); + } + return; } - EarlyScope(lifetimes, _, s) | - LateScope(lifetimes, s) => { - search_result = search_lifetimes(lifetimes, lifetime_ref); - if search_result.is_some() { - break; - } + Scope::ObjectLifetimeDefault { s, .. } => { scope = s; } } - } + }; - match search_result { - Some((_depth, lifetime)) => { - let def = DefFreeRegion(scope_data, lifetime.id); - self.insert_lifetime(lifetime_ref, def); + let mut err = struct_span_err!(self.sess, span, E0106, + "missing lifetime specifier{}", + if lifetime_refs.len() > 1 { "s" } else { "" }); + let msg = if lifetime_refs.len() > 1 { + format!("expected {} lifetime parameters", lifetime_refs.len()) + } else { + format!("expected lifetime parameter") + }; + err.span_label(span, &msg); + + if let Some(params) = error { + if lifetime_refs.len() == 1 { + self.report_elision_failure(&mut err, params); } + } + err.emit(); + } - None => { - self.unresolved_lifetime_ref(lifetime_ref); + fn report_elision_failure(&mut self, + db: &mut DiagnosticBuilder, + params: &[ElisionFailureInfo]) { + let mut m = String::new(); + let len = params.len(); + + let elided_params: Vec<_> = params.iter().cloned() + .filter(|info| info.lifetime_count > 0) + .collect(); + + let elided_len = elided_params.len(); + + for (i, info) in elided_params.into_iter().enumerate() { + let ElisionFailureInfo { + parent, index, lifetime_count: n, have_bound_regions + } = info; + + let help_name = if let Some(body) = parent { + let arg = &self.hir_map.body(body).arguments[index]; + format!("`{}`", self.hir_map.node_to_pretty_string(arg.pat.id)) + } else { + format!("argument {}", index + 1) + }; + + m.push_str(&(if n == 1 { + help_name + } else { + format!("one of {}'s {} elided {}lifetimes", help_name, n, + if have_bound_regions { "free " } else { "" } ) + })[..]); + + if elided_len == 2 && i == 0 { + m.push_str(" or "); + } else if i + 2 == elided_len { + m.push_str(", or "); + } else if i != elided_len - 1 { + m.push_str(", "); } + } + if len == 0 { + help!(db, + "this function's return type contains a borrowed value, but \ + there is no value for it to be borrowed from"); + help!(db, + "consider giving it a 'static lifetime"); + } else if elided_len == 0 { + help!(db, + "this function's return type contains a borrowed value with \ + an elided lifetime, but the lifetime cannot be derived from \ + the arguments"); + help!(db, + "consider giving it an explicit bounded or 'static \ + lifetime"); + } else if elided_len == 1 { + help!(db, + "this function's return type contains a borrowed value, but \ + the signature does not say which {} it is borrowed from", + m); + } else { + help!(db, + "this function's return type contains a borrowed value, but \ + the signature does not say whether it is borrowed from {}", + m); + } } - fn unresolved_lifetime_ref(&self, lifetime_ref: &hir::Lifetime) { - struct_span_err!(self.sess, lifetime_ref.span, E0261, - "use of undeclared lifetime name `{}`", lifetime_ref.name) - .span_label(lifetime_ref.span, &format!("undeclared lifetime")) - .emit(); + fn resolve_object_lifetime_default(&mut self, lifetime_ref: &hir::Lifetime) { + let mut late_depth = 0; + let mut scope = self.scope; + let lifetime = loop { + match *scope { + Scope::Binder { s, .. } => { + late_depth += 1; + scope = s; + } + + Scope::Root | + Scope::Elision { .. } => break Region::Static, + + Scope::Body { .. } | + Scope::ObjectLifetimeDefault { lifetime: None, .. } => return, + + Scope::ObjectLifetimeDefault { lifetime: Some(l), .. } => break l + } + }; + self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth)); } - fn check_lifetime_defs(&mut self, old_scope: Scope, lifetimes: &[hir::LifetimeDef]) { + fn check_lifetime_defs(&mut self, old_scope: ScopeRef, lifetimes: &[hir::LifetimeDef]) { for i in 0..lifetimes.len() { let lifetime_i = &lifetimes[i]; @@ -770,7 +1475,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } fn check_lifetime_def_for_shadowing(&self, - mut old_scope: Scope, + mut old_scope: ScopeRef, lifetime: &hir::Lifetime) { for &(label, label_span) in &self.labels_in_fn { @@ -786,21 +1491,22 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { loop { match *old_scope { - FnScope { s, .. } => { + Scope::Body { s, .. } | + Scope::Elision { s, .. } | + Scope::ObjectLifetimeDefault { s, .. } => { old_scope = s; } - RootScope => { + Scope::Root => { return; } - EarlyScope(lifetimes, _, s) | - LateScope(lifetimes, s) => { - if let Some((_, lifetime_def)) = search_lifetimes(lifetimes, lifetime) { + Scope::Binder { ref lifetimes, s } => { + if let Some(&def) = lifetimes.get(&lifetime.name) { signal_shadowing_problem( self.sess, lifetime.name, - original_lifetime(&lifetime_def), + original_lifetime(self.hir_map.span(def.id().unwrap())), shadower_lifetime(&lifetime)); return; } @@ -813,7 +1519,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { fn insert_lifetime(&mut self, lifetime_ref: &hir::Lifetime, - def: DefRegion) { + def: Region) { if lifetime_ref.id == ast::DUMMY_NODE_ID { span_bug!(lifetime_ref.span, "lifetime reference not renumbered, \ @@ -828,17 +1534,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } } -fn search_lifetimes<'a>(lifetimes: &'a [hir::LifetimeDef], - lifetime_ref: &hir::Lifetime) - -> Option<(u32, &'a hir::Lifetime)> { - for (i, lifetime_decl) in lifetimes.iter().enumerate() { - if lifetime_decl.lifetime.name == lifetime_ref.name { - return Some((i as u32, &lifetime_decl.lifetime)); - } - } - return None; -} - /////////////////////////////////////////////////////////////////////////// /// Detects late-bound lifetimes and inserts them into diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 36fc5149b40d8..cc813d0bf831e 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -592,24 +592,6 @@ pub enum IntVarValue { UintType(ast::UintTy), } -/// Default region to use for the bound of objects that are -/// supplied as the value for this type parameter. This is derived -/// from `T:'a` annotations appearing in the type definition. If -/// this is `None`, then the default is inherited from the -/// surrounding context. See RFC #599 for details. -#[derive(Copy, Clone, RustcEncodable, RustcDecodable)] -pub enum ObjectLifetimeDefault<'tcx> { - /// Require an explicit annotation. Occurs when multiple - /// `T:'a` constraints are found. - Ambiguous, - - /// Use the base default, typically 'static, but in a fn body it is a fresh variable - BaseDefault, - - /// Use the given region as the default. - Specific(&'tcx Region), -} - #[derive(Clone, RustcEncodable, RustcDecodable)] pub struct TypeParameterDef<'tcx> { pub name: Name, @@ -617,7 +599,6 @@ pub struct TypeParameterDef<'tcx> { pub index: u32, pub default_def_id: DefId, // for use in error reporing about defaults pub default: Option>, - pub object_lifetime_default: ObjectLifetimeDefault<'tcx>, /// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute /// on generic parameter `T`, asserts data behind the parameter @@ -625,12 +606,11 @@ pub struct TypeParameterDef<'tcx> { pub pure_wrt_drop: bool, } -#[derive(Clone, RustcEncodable, RustcDecodable)] -pub struct RegionParameterDef<'tcx> { +#[derive(Copy, Clone, RustcEncodable, RustcDecodable)] +pub struct RegionParameterDef { pub name: Name, pub def_id: DefId, pub index: u32, - pub bounds: Vec<&'tcx ty::Region>, /// `pure_wrt_drop`, set by the (unsafe) `#[may_dangle]` attribute /// on generic parameter `'a`, asserts data of lifetime `'a` @@ -638,7 +618,7 @@ pub struct RegionParameterDef<'tcx> { pub pure_wrt_drop: bool, } -impl<'tcx> RegionParameterDef<'tcx> { +impl RegionParameterDef { pub fn to_early_bound_region_data(&self) -> ty::EarlyBoundRegion { ty::EarlyBoundRegion { index: self.index, @@ -659,7 +639,7 @@ pub struct Generics<'tcx> { pub parent: Option, pub parent_regions: u32, pub parent_types: u32, - pub regions: Vec>, + pub regions: Vec, pub types: Vec>, pub has_self: bool, } @@ -677,7 +657,7 @@ impl<'tcx> Generics<'tcx> { self.parent_count() + self.own_count() } - pub fn region_param(&self, param: &EarlyBoundRegion) -> &RegionParameterDef<'tcx> { + pub fn region_param(&self, param: &EarlyBoundRegion) -> &RegionParameterDef { &self.regions[param.index as usize - self.has_self as usize] } diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 0f0478bc8cdb0..06ba1b2a1fafc 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -353,7 +353,7 @@ macro_rules! CopyImpls { } } -CopyImpls! { (), hir::Unsafety, abi::Abi } +CopyImpls! { (), hir::Unsafety, abi::Abi, ty::RegionParameterDef } impl<'tcx, T:TypeFoldable<'tcx>, U:TypeFoldable<'tcx>> TypeFoldable<'tcx> for (T, U) { fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> (T, U) { @@ -726,52 +726,12 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TypeParameterDef<'tcx> { index: self.index, default: self.default.fold_with(folder), default_def_id: self.default_def_id, - object_lifetime_default: self.object_lifetime_default.fold_with(folder), pure_wrt_drop: self.pure_wrt_drop, } } fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.default.visit_with(visitor) || - self.object_lifetime_default.visit_with(visitor) - } -} - -impl<'tcx> TypeFoldable<'tcx> for ty::ObjectLifetimeDefault<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - match *self { - ty::ObjectLifetimeDefault::Ambiguous => - ty::ObjectLifetimeDefault::Ambiguous, - - ty::ObjectLifetimeDefault::BaseDefault => - ty::ObjectLifetimeDefault::BaseDefault, - - ty::ObjectLifetimeDefault::Specific(r) => - ty::ObjectLifetimeDefault::Specific(r.fold_with(folder)), - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - match *self { - ty::ObjectLifetimeDefault::Specific(r) => r.visit_with(visitor), - _ => false, - } - } -} - -impl<'tcx> TypeFoldable<'tcx> for ty::RegionParameterDef<'tcx> { - fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self { - ty::RegionParameterDef { - name: self.name, - def_id: self.def_id, - index: self.index, - bounds: self.bounds.fold_with(folder), - pure_wrt_drop: self.pure_wrt_drop, - } - } - - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.bounds.visit_with(visitor) + self.default.visit_with(visitor) } } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index e90e1a94be951..74e27f84fddc2 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -336,13 +336,12 @@ impl<'tcx> fmt::Debug for ty::TypeParameterDef<'tcx> { } } -impl<'tcx> fmt::Debug for ty::RegionParameterDef<'tcx> { +impl fmt::Debug for ty::RegionParameterDef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "RegionParameterDef({}, {:?}, {}, {:?})", + write!(f, "RegionParameterDef({}, {:?}, {})", self.name, self.def_id, - self.index, - self.bounds) + self.index) } } @@ -523,16 +522,6 @@ impl<'tcx> fmt::Debug for ty::ParameterEnvironment<'tcx> { } } -impl<'tcx> fmt::Debug for ty::ObjectLifetimeDefault<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ty::ObjectLifetimeDefault::Ambiguous => write!(f, "Ambiguous"), - ty::ObjectLifetimeDefault::BaseDefault => write!(f, "BaseDefault"), - ty::ObjectLifetimeDefault::Specific(ref r) => write!(f, "{:?}", r), - } - } -} - impl fmt::Display for ty::Region { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if verbose() { diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 4ce6c9527571e..46179b31d5cb4 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -939,12 +939,12 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { /// Given a type, if it is an immutable reference, return a suggestion to make it mutable fn suggest_mut_for_immutable(&self, pty: &hir::Ty) -> Option { // Check wether the argument is an immutable reference - if let hir::TyRptr(opt_lifetime, hir::MutTy { + if let hir::TyRptr(lifetime, hir::MutTy { mutbl: hir::Mutability::MutImmutable, ref ty }) = pty.node { // Account for existing lifetimes when generating the message - if let Some(lifetime) = opt_lifetime { + if !lifetime.is_elided() { if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(ty.span) { if let Ok(lifetime_snippet) = self.tcx.sess.codemap() .span_to_snippet(lifetime.span) { diff --git a/src/librustc_incremental/calculate_svh/svh_visitor.rs b/src/librustc_incremental/calculate_svh/svh_visitor.rs index 69237f406760d..c7512f2971b33 100644 --- a/src/librustc_incremental/calculate_svh/svh_visitor.rs +++ b/src/librustc_incremental/calculate_svh/svh_visitor.rs @@ -828,7 +828,7 @@ impl<'a, 'hash, 'tcx> visit::Visitor<'tcx> for StrictVersionHashVisitor<'a, 'has visit::walk_ty_param_bound(self, bounds) } - fn visit_poly_trait_ref(&mut self, t: &'tcx PolyTraitRef, m: &'tcx TraitBoundModifier) { + fn visit_poly_trait_ref(&mut self, t: &'tcx PolyTraitRef, m: TraitBoundModifier) { debug!("visit_poly_trait_ref: st={:?}", self.st); SawPolyTraitRef.hash(self.st); m.hash(self.st); diff --git a/src/librustc_lint/bad_style.rs b/src/librustc_lint/bad_style.rs index d4ab31da8a31e..05ba262ef90c0 100644 --- a/src/librustc_lint/bad_style.rs +++ b/src/librustc_lint/bad_style.rs @@ -377,8 +377,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonUpperCaseGlobals { fn check_pat(&mut self, cx: &LateContext, p: &hir::Pat) { // Lint for constants that look like binding identifiers (#7526) if let PatKind::Path(hir::QPath::Resolved(None, ref path)) = p.node { - if path.segments.len() == 1 && path.segments[0].parameters.is_empty() { - if let Def::Const(..) = path.def { + if let Def::Const(..) = path.def { + if path.segments.len() == 1 { NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern", path.segments[0].name, diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index d100cb53a8b01..39581a4696088 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -17,6 +17,7 @@ use rustc::middle::cstore::{CrateStore, CrateSource, LibSource, DepKind, ExternC use rustc::middle::cstore::{NativeLibrary, LinkMeta, LinkagePreference, LoadedMacro}; use rustc::hir::def::{self, Def}; use rustc::middle::lang_items; +use rustc::middle::resolve_lifetime::ObjectLifetimeDefault; use rustc::session::Session; use rustc::ty::{self, Ty, TyCtxt}; use rustc::hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; @@ -110,6 +111,17 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { self.get_crate_data(def.krate).get_generics(def.index, tcx) } + fn item_generics_own_param_counts(&self, def: DefId) -> (usize, usize) { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).generics_own_param_counts(def.index) + } + + fn item_generics_object_lifetime_defaults(&self, def: DefId) + -> Vec { + self.dep_graph.read(DepNode::MetaData(def)); + self.get_crate_data(def.krate).generics_object_lifetime_defaults(def.index) + } + fn item_attrs(&self, def_id: DefId) -> Vec { self.dep_graph.read(DepNode::MetaData(def_id)); diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 101531b52afb8..dd44ef202dc27 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -20,6 +20,7 @@ use rustc::middle::cstore::LinkagePreference; use rustc::hir::def::{self, Def, CtorKind}; use rustc::hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc::middle::lang_items; +use rustc::middle::resolve_lifetime::ObjectLifetimeDefault; use rustc::session::Session; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; @@ -598,7 +599,26 @@ impl<'a, 'tcx> CrateMetadata { item_id: DefIndex, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> ty::Generics<'tcx> { - self.entry(item_id).generics.unwrap().decode((self, tcx)) + let g = self.entry(item_id).generics.unwrap().decode(self); + ty::Generics { + parent: g.parent, + parent_regions: g.parent_regions, + parent_types: g.parent_types, + regions: g.regions.decode((self, tcx)).collect(), + types: g.types.decode((self, tcx)).collect(), + has_self: g.has_self, + } + } + + pub fn generics_own_param_counts(&self, item_id: DefIndex) -> (usize, usize) { + let g = self.entry(item_id).generics.unwrap().decode(self); + (g.regions.len, g.types.len) + } + + pub fn generics_object_lifetime_defaults(&self, item_id: DefIndex) + -> Vec { + self.entry(item_id).generics.unwrap().decode(self) + .object_lifetime_defaults.decode(self).collect() } pub fn get_type(&self, id: DefIndex, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Ty<'tcx> { diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 2f71776ecf758..028555d1df848 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -417,9 +417,26 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } - fn encode_generics(&mut self, def_id: DefId) -> Lazy> { + fn encode_generics(&mut self, def_id: DefId) -> Lazy> { let tcx = self.tcx; - self.lazy(tcx.item_generics(def_id)) + let g = tcx.item_generics(def_id); + let regions = self.lazy_seq_ref(&g.regions); + let types = self.lazy_seq_ref(&g.types); + let mut object_lifetime_defaults = LazySeq::empty(); + if let Some(id) = tcx.hir.as_local_node_id(def_id) { + if let Some(o) = tcx.named_region_map.object_lifetime_defaults.get(&id) { + object_lifetime_defaults = self.lazy_seq_ref(o); + } + } + self.lazy(&Generics { + parent: g.parent, + parent_regions: g.parent_regions, + parent_types: g.parent_types, + regions: regions, + types: types, + has_self: g.has_self, + object_lifetime_defaults: object_lifetime_defaults, + }) } fn encode_predicates(&mut self, def_id: DefId) -> Lazy> { diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index 74825a5c6e3f6..d9c639f2bc573 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -16,6 +16,7 @@ use rustc::hir::def::{self, CtorKind}; use rustc::hir::def_id::{DefIndex, DefId}; use rustc::middle::cstore::{DepKind, LinkagePreference, NativeLibrary}; use rustc::middle::lang_items; +use rustc::middle::resolve_lifetime::ObjectLifetimeDefault; use rustc::mir; use rustc::ty::{self, Ty}; use rustc_back::PanicStrategy; @@ -213,7 +214,7 @@ pub struct Entry<'tcx> { pub ty: Option>>, pub inherent_impls: LazySeq, pub variances: LazySeq, - pub generics: Option>>, + pub generics: Option>>, pub predicates: Option>>, pub ast: Option>>, @@ -247,6 +248,20 @@ pub enum EntryKind<'tcx> { AssociatedConst(AssociatedContainer), } +/// A copy of `ty::Generics` which allows lazy decoding of +/// `regions` and `types` (e.g. knowing the number of type +/// and lifetime parameters before `TyCtxt` is created). +#[derive(RustcEncodable, RustcDecodable)] +pub struct Generics<'tcx> { + pub parent: Option, + pub parent_regions: u32, + pub parent_types: u32, + pub regions: LazySeq, + pub types: LazySeq>, + pub has_self: bool, + pub object_lifetime_defaults: LazySeq, +} + #[derive(RustcEncodable, RustcDecodable)] pub struct ModData { pub reexports: LazySeq, diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 9720bb8426475..0933fdfd357cd 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -144,6 +144,17 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }); } TyKind::TraitObject(ref bounds) => { + let mut any_lifetime_bounds = false; + for bound in bounds { + if let RegionTyParamBound(ref lifetime) = *bound { + if any_lifetime_bounds { + span_err!(self.session, lifetime.span, E0226, + "only a single explicit lifetime bound is permitted"); + break; + } + any_lifetime_bounds = true; + } + } self.no_questions_in_bounds(bounds, "trait object types", false); } TyKind::ImplTrait(ref bounds) => { diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index c414d71836818..ef871959176af 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -244,6 +244,7 @@ match 5u32 { } register_diagnostics! { + E0226, // only a single explicit lifetime bound is permitted E0472, // asm! is unsupported on this target E0561, // patterns aren't allowed in function pointer types E0571, // `break` with a value in a non-`loop`-loop diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 1af8b59cdfa7d..56de539cbfe99 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -10,7 +10,7 @@ //! Conversion from AST representation of types to the ty.rs //! representation. The main routine here is `ast_ty_to_ty()`: each use -//! is parameterized by an instance of `AstConv` and a `RegionScope`. +//! is parameterized by an instance of `AstConv`. //! //! The parameterization of `ast_ty_to_ty()` is because it behaves //! somewhat differently during the collect and check phases, @@ -22,31 +22,6 @@ //! an error). In the check phase, when the FnCtxt is used as the //! `AstConv`, `get_item_type()` just looks up the item type in //! `tcx.types` (using `TyCtxt::item_type`). -//! -//! The `RegionScope` trait controls what happens when the user does -//! not specify a region in some location where a region is required -//! (e.g., if the user writes `&Foo` as a type rather than `&'a Foo`). -//! See the `rscope` module for more details. -//! -//! Unlike the `AstConv` trait, the region scope can change as we descend -//! the type. This is to accommodate the fact that (a) fn types are binding -//! scopes and (b) the default region may change. To understand case (a), -//! consider something like: -//! -//! type foo = { x: &a.int, y: |&a.int| } -//! -//! The type of `x` is an error because there is no region `a` in scope. -//! In the type of `y`, however, region `a` is considered a bound region -//! as it does not already appear in scope. -//! -//! Case (b) says that if you have a type: -//! type foo<'a> = ...; -//! type bar = fn(&foo, &a.foo) -//! The fully expanded version of type bar is: -//! type bar = fn(&'foo &, &a.foo<'a>) -//! Note that the self region for the `foo` defaulted to `&` in the first -//! case but `&a` in the second. Basically, defaults that appear inside -//! an rptr (`&r.T`) use the region `r` that appears in the rptr. use rustc_const_eval::eval_length; use rustc_data_structures::accumulate_vec::AccumulateVec; @@ -61,10 +36,6 @@ use rustc::ty::{self, Ty, TyCtxt, ToPredicate, TypeFoldable}; use rustc::ty::wf::object_region_bounds; use rustc_back::slice; use require_c_abi_if_variadic; -use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope, - ObjectLifetimeDefaultRscope, ShiftedRscope, BindingRscope, - ElisionFailureInfo, ElidedLifetime}; -use rscope::{AnonTypeScope, MaybeWithAnonTypes}; use util::common::{ErrorReported, FN_OUTPUT_NAME}; use util::nodemap::{NodeMap, FxHashSet}; @@ -74,7 +45,6 @@ use syntax::{abi, ast}; use syntax::feature_gate::{GateIssue, emit_feature_err}; use syntax::symbol::{Symbol, keywords}; use syntax_pos::Span; -use errors::DiagnosticBuilder; pub trait AstConv<'gcx, 'tcx> { fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx>; @@ -111,6 +81,10 @@ pub trait AstConv<'gcx, 'tcx> { /// See ParameterEnvironment::free_substs for more information. fn get_free_substs(&self) -> Option<&Substs<'tcx>>; + /// What lifetime should we use when a lifetime is omitted (and not elided)? + fn re_infer(&self, span: Span, _def: Option<&ty::RegionParameterDef>) + -> Option<&'tcx ty::Region>; + /// What type should we use when a type is omitted? fn ty_infer(&self, span: Span) -> Ty<'tcx>; @@ -161,171 +135,71 @@ struct ConvertedBinding<'tcx> { /// This type must not appear anywhere in other converted types. const TRAIT_OBJECT_DUMMY_SELF: ty::TypeVariants<'static> = ty::TyInfer(ty::FreshTy(0)); -pub fn ast_region_to_region<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - lifetime: &hir::Lifetime) - -> &'tcx ty::Region { - let r = match tcx.named_region_map.defs.get(&lifetime.id) { - None => { - // should have been recorded by the `resolve_lifetime` pass - span_bug!(lifetime.span, "unresolved lifetime"); - } +impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { + pub fn ast_region_to_region(&self, + lifetime: &hir::Lifetime, + def: Option<&ty::RegionParameterDef>) + -> &'tcx ty::Region + { + let tcx = self.tcx(); + let r = match tcx.named_region_map.defs.get(&lifetime.id) { + Some(&rl::Region::Static) => { + tcx.mk_region(ty::ReStatic) + } - Some(&rl::DefStaticRegion) => { - ty::ReStatic - } + Some(&rl::Region::LateBound(debruijn, id)) => { + // If this region is declared on a function, it will have + // an entry in `late_bound`, but if it comes from + // `for<'a>` in some type or something, it won't + // necessarily have one. In that case though, we won't be + // changed from late to early bound, so we can just + // substitute false. + let issue_32330 = tcx.named_region_map + .late_bound + .get(&id) + .cloned() + .unwrap_or(ty::Issue32330::WontChange); + let name = tcx.hir.name(id); + tcx.mk_region(ty::ReLateBound(debruijn, + ty::BrNamed(tcx.hir.local_def_id(id), name, issue_32330))) + } - Some(&rl::DefLateBoundRegion(debruijn, id)) => { - // If this region is declared on a function, it will have - // an entry in `late_bound`, but if it comes from - // `for<'a>` in some type or something, it won't - // necessarily have one. In that case though, we won't be - // changed from late to early bound, so we can just - // substitute false. - let issue_32330 = tcx.named_region_map - .late_bound - .get(&id) - .cloned() - .unwrap_or(ty::Issue32330::WontChange); - ty::ReLateBound(debruijn, ty::BrNamed(tcx.hir.local_def_id(id), - lifetime.name, - issue_32330)) - } + Some(&rl::Region::LateBoundAnon(debruijn, index)) => { + tcx.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(index))) + } - Some(&rl::DefEarlyBoundRegion(index, _)) => { - ty::ReEarlyBound(ty::EarlyBoundRegion { - index: index, - name: lifetime.name - }) - } + Some(&rl::Region::EarlyBound(index, id)) => { + let name = tcx.hir.name(id); + tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { + index: index, + name: name + })) + } - Some(&rl::DefFreeRegion(scope, id)) => { - // As in DefLateBoundRegion above, could be missing for some late-bound - // regions, but also for early-bound regions. - let issue_32330 = tcx.named_region_map - .late_bound - .get(&id) - .cloned() - .unwrap_or(ty::Issue32330::WontChange); - ty::ReFree(ty::FreeRegion { + Some(&rl::Region::Free(scope, id)) => { + // As in Region::LateBound above, could be missing for some late-bound + // regions, but also for early-bound regions. + let issue_32330 = tcx.named_region_map + .late_bound + .get(&id) + .cloned() + .unwrap_or(ty::Issue32330::WontChange); + let name = tcx.hir.name(id); + tcx.mk_region(ty::ReFree(ty::FreeRegion { scope: scope.to_code_extent(&tcx.region_maps), - bound_region: ty::BrNamed(tcx.hir.local_def_id(id), - lifetime.name, - issue_32330) - }) - - // (*) -- not late-bound, won't change - } - }; - - debug!("ast_region_to_region(lifetime={:?} id={}) yields {:?}", - lifetime, - lifetime.id, - r); + bound_region: ty::BrNamed(tcx.hir.local_def_id(id), name, issue_32330) + })) - tcx.mk_region(r) -} - -fn report_elision_failure( - tcx: TyCtxt, - db: &mut DiagnosticBuilder, - params: Vec) -{ - let mut m = String::new(); - let len = params.len(); - - let elided_params: Vec<_> = params.into_iter() - .filter(|info| info.lifetime_count > 0) - .collect(); - - let elided_len = elided_params.len(); - - for (i, info) in elided_params.into_iter().enumerate() { - let ElisionFailureInfo { - parent, index, lifetime_count: n, have_bound_regions - } = info; - - let help_name = if let Some(body) = parent { - let arg = &tcx.hir.body(body).arguments[index]; - format!("`{}`", tcx.hir.node_to_pretty_string(arg.pat.id)) - } else { - format!("argument {}", index + 1) - }; - - m.push_str(&(if n == 1 { - help_name - } else { - format!("one of {}'s {} elided {}lifetimes", help_name, n, - if have_bound_regions { "free " } else { "" } ) - })[..]); - - if elided_len == 2 && i == 0 { - m.push_str(" or "); - } else if i + 2 == elided_len { - m.push_str(", or "); - } else if i != elided_len - 1 { - m.push_str(", "); - } - - } - - if len == 0 { - help!(db, - "this function's return type contains a borrowed value, but \ - there is no value for it to be borrowed from"); - help!(db, - "consider giving it a 'static lifetime"); - } else if elided_len == 0 { - help!(db, - "this function's return type contains a borrowed value with \ - an elided lifetime, but the lifetime cannot be derived from \ - the arguments"); - help!(db, - "consider giving it an explicit bounded or 'static \ - lifetime"); - } else if elided_len == 1 { - help!(db, - "this function's return type contains a borrowed value, but \ - the signature does not say which {} it is borrowed from", - m); - } else { - help!(db, - "this function's return type contains a borrowed value, but \ - the signature does not say whether it is borrowed from {}", - m); - } -} - -impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { - pub fn opt_ast_region_to_region(&self, - rscope: &RegionScope, - default_span: Span, - opt_lifetime: &Option) -> &'tcx ty::Region - { - let r = match *opt_lifetime { - Some(ref lifetime) => { - ast_region_to_region(self.tcx(), lifetime) + // (*) -- not late-bound, won't change } - None => self.tcx().mk_region(match rscope.anon_regions(default_span, 1) { - Ok(rs) => rs[0], - Err(params) => { - let ampersand_span = Span { hi: default_span.lo, ..default_span}; - - let mut err = struct_span_err!(self.tcx().sess, ampersand_span, E0106, - "missing lifetime specifier"); - err.span_label(ampersand_span, &format!("expected lifetime parameter")); - - if let Some(params) = params { - report_elision_failure(self.tcx(), &mut err, params); - } - err.emit(); - ty::ReStatic - } - }) + None => { + self.re_infer(lifetime.span, def).expect("unelided lifetime in signature") + } }; - debug!("opt_ast_region_to_region(opt_lifetime={:?}) yields {:?}", - opt_lifetime, + debug!("ast_region_to_region(lifetime={:?}) yields {:?}", + lifetime, r); r @@ -334,7 +208,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`, /// returns an appropriate set of substitutions for this particular reference to `I`. pub fn ast_path_substs_for_ty(&self, - rscope: &RegionScope, span: Span, def_id: DefId, item_segment: &hir::PathSegment) @@ -359,8 +232,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } let (substs, assoc_bindings) = - self.create_substs_for_ast_path(rscope, - span, + self.create_substs_for_ast_path(span, def_id, &item_segment.parameters, None); @@ -376,7 +248,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { /// /// Note that the type listing given here is *exactly* what the user provided. fn create_substs_for_ast_path(&self, - rscope: &RegionScope, span: Span, def_id: DefId, parameters: &hir::PathParameters, @@ -409,23 +280,11 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { }; let expected_num_region_params = decl_generics.regions.len(); let supplied_num_region_params = lifetimes.len(); - let regions = if expected_num_region_params == supplied_num_region_params { - lifetimes.iter().map(|l| *ast_region_to_region(tcx, l)).collect() - } else { - let anon_regions = - rscope.anon_regions(span, expected_num_region_params); - - if supplied_num_region_params != 0 || anon_regions.is_err() { - report_lifetime_number_error(tcx, span, - supplied_num_region_params, - expected_num_region_params); - } - - match anon_regions { - Ok(anon_regions) => anon_regions, - Err(_) => (0..expected_num_region_params).map(|_| ty::ReStatic).collect() - } - }; + if expected_num_region_params != supplied_num_region_params { + report_lifetime_number_error(tcx, span, + supplied_num_region_params, + expected_num_region_params); + } // If a self-type was declared, one should be provided. assert_eq!(decl_generics.has_self, self_ty.is_some()); @@ -452,7 +311,11 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let mut output_assoc_binding = None; let substs = Substs::for_item(tcx, def_id, |def, _| { let i = def.index as usize - self_ty.is_some() as usize; - tcx.mk_region(regions[i]) + if let Some(lifetime) = lifetimes.get(i) { + self.ast_region_to_region(lifetime, Some(def)) + } else { + tcx.mk_region(ty::ReStatic) + } }, |def, substs| { let i = def.index as usize; @@ -466,12 +329,11 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { // A provided type parameter. match *parameters { hir::AngleBracketedParameters(ref data) => { - self.ast_ty_arg_to_ty(rscope, Some(def), substs, &data.types[i]) + self.ast_ty_to_ty(&data.types[i]) } hir::ParenthesizedParameters(ref data) => { assert_eq!(i, 0); - let (ty, assoc) = - self.convert_parenthesized_parameters(rscope, substs, data); + let (ty, assoc) = self.convert_parenthesized_parameters(data); output_assoc_binding = Some(assoc); ty } @@ -516,7 +378,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { data.bindings.iter().map(|b| { ConvertedBinding { item_name: b.name, - ty: self.ast_ty_to_ty(rscope, &b.ty), + ty: self.ast_ty_to_ty(&b.ty), span: b.span } }).collect() @@ -525,7 +387,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { vec![output_assoc_binding.unwrap_or_else(|| { // This is an error condition, but we should // get the associated type binding anyway. - self.convert_parenthesized_parameters(rscope, substs, data).1 + self.convert_parenthesized_parameters(data).1 })] } }; @@ -536,92 +398,17 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { (substs, assoc_bindings) } - /// Returns the appropriate lifetime to use for any output lifetimes - /// (if one exists) and a vector of the (pattern, number of lifetimes) - /// corresponding to each input type/pattern. - fn find_implied_output_region(&self, - input_tys: &[Ty<'tcx>], - parent: Option, - input_indices: I) -> ElidedLifetime - where I: Iterator - { - let tcx = self.tcx(); - let mut lifetimes_for_params = Vec::with_capacity(input_tys.len()); - let mut possible_implied_output_region = None; - let mut lifetimes = 0; - - for (input_type, index) in input_tys.iter().zip(input_indices) { - let mut regions = FxHashSet(); - let have_bound_regions = tcx.collect_regions(input_type, &mut regions); - - debug!("find_implied_output_regions: collected {:?} from {:?} \ - have_bound_regions={:?}", ®ions, input_type, have_bound_regions); - - lifetimes += regions.len(); - - if lifetimes == 1 && regions.len() == 1 { - // there's a chance that the unique lifetime of this - // iteration will be the appropriate lifetime for output - // parameters, so lets store it. - possible_implied_output_region = regions.iter().cloned().next(); - } - - lifetimes_for_params.push(ElisionFailureInfo { - parent: parent, - index: index, - lifetime_count: regions.len(), - have_bound_regions: have_bound_regions - }); - } - - if lifetimes == 1 { - Ok(*possible_implied_output_region.unwrap()) - } else { - Err(Some(lifetimes_for_params)) - } - } - - fn convert_ty_with_lifetime_elision(&self, - elided_lifetime: ElidedLifetime, - ty: &hir::Ty, - anon_scope: Option) - -> Ty<'tcx> - { - match elided_lifetime { - Ok(implied_output_region) => { - let rb = ElidableRscope::new(implied_output_region); - self.ast_ty_to_ty(&MaybeWithAnonTypes::new(rb, anon_scope), ty) - } - Err(param_lifetimes) => { - // All regions must be explicitly specified in the output - // if the lifetime elision rules do not apply. This saves - // the user from potentially-confusing errors. - let rb = UnelidableRscope::new(param_lifetimes); - self.ast_ty_to_ty(&MaybeWithAnonTypes::new(rb, anon_scope), ty) - } - } - } - fn convert_parenthesized_parameters(&self, - rscope: &RegionScope, - region_substs: &[Kind<'tcx>], data: &hir::ParenthesizedParameterData) -> (Ty<'tcx>, ConvertedBinding<'tcx>) { - let anon_scope = rscope.anon_type_scope(); - let binding_rscope = MaybeWithAnonTypes::new(BindingRscope::new(), anon_scope); let inputs = self.tcx().mk_type_list(data.inputs.iter().map(|a_t| { - self.ast_ty_arg_to_ty(&binding_rscope, None, region_substs, a_t) + self.ast_ty_to_ty(a_t) })); - let input_params = 0..inputs.len(); - let implied_output_region = self.find_implied_output_region(&inputs, None, input_params); let (output, output_span) = match data.output { Some(ref output_ty) => { - (self.convert_ty_with_lifetime_elision(implied_output_region, - &output_ty, - anon_scope), - output_ty.span) + (self.ast_ty_to_ty(output_ty), output_ty.span) } None => { (self.tcx().mk_nil(), data.span) @@ -637,24 +424,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { (self.tcx().mk_ty(ty::TyTuple(inputs)), output_binding) } - pub fn instantiate_poly_trait_ref(&self, - rscope: &RegionScope, - ast_trait_ref: &hir::PolyTraitRef, - self_ty: Ty<'tcx>, - poly_projections: &mut Vec>) - -> ty::PolyTraitRef<'tcx> - { - let trait_ref = &ast_trait_ref.trait_ref; - let trait_def_id = self.trait_def_id(trait_ref); - self.ast_path_to_poly_trait_ref(rscope, - trait_ref.path.span, - trait_def_id, - self_ty, - trait_ref.ref_id, - trait_ref.path.segments.last().unwrap(), - poly_projections) - } - /// Instantiates the path for the given trait reference, assuming that it's /// bound to a valid trait type. Returns the def_id for the defining trait. /// Fails if the type is a type other than a trait type. @@ -662,14 +431,12 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { /// If the `projections` argument is `None`, then assoc type bindings like `Foo` /// are disallowed. Otherwise, they are pushed onto the vector given. pub fn instantiate_mono_trait_ref(&self, - rscope: &RegionScope, trait_ref: &hir::TraitRef, self_ty: Ty<'tcx>) -> ty::TraitRef<'tcx> { let trait_def_id = self.trait_def_id(trait_ref); - self.ast_path_to_mono_trait_ref(rscope, - trait_ref.path.span, + self.ast_path_to_mono_trait_ref(trait_ref.path.span, trait_def_id, self_ty, trait_ref.path.segments.last().unwrap()) @@ -689,48 +456,39 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } } - fn ast_path_to_poly_trait_ref(&self, - rscope: &RegionScope, - span: Span, - trait_def_id: DefId, + pub fn instantiate_poly_trait_ref(&self, + ast_trait_ref: &hir::PolyTraitRef, self_ty: Ty<'tcx>, - path_id: ast::NodeId, - trait_segment: &hir::PathSegment, poly_projections: &mut Vec>) -> ty::PolyTraitRef<'tcx> { - debug!("ast_path_to_poly_trait_ref(trait_segment={:?})", trait_segment); - // The trait reference introduces a binding level here, so - // we need to shift the `rscope`. It'd be nice if we could - // do away with this rscope stuff and work this knowledge - // into resolve_lifetimes, as we do with non-omitted - // lifetimes. Oh well, not there yet. - let shifted_rscope = &ShiftedRscope::new(rscope); + let trait_ref = &ast_trait_ref.trait_ref; + let trait_def_id = self.trait_def_id(trait_ref); + + debug!("ast_path_to_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id); let (substs, assoc_bindings) = - self.create_substs_for_ast_trait_ref(shifted_rscope, - span, + self.create_substs_for_ast_trait_ref(trait_ref.path.span, trait_def_id, self_ty, - trait_segment); + trait_ref.path.segments.last().unwrap()); let poly_trait_ref = ty::Binder(ty::TraitRef::new(trait_def_id, substs)); poly_projections.extend(assoc_bindings.iter().filter_map(|binding| { // specify type to assert that error was already reported in Err case: let predicate: Result<_, ErrorReported> = - self.ast_type_binding_to_poly_projection_predicate(path_id, + self.ast_type_binding_to_poly_projection_predicate(trait_ref.ref_id, poly_trait_ref, binding); predicate.ok() // ok to ignore Err() because ErrorReported (see above) })); - debug!("ast_path_to_poly_trait_ref(trait_segment={:?}, projections={:?}) -> {:?}", - trait_segment, poly_projections, poly_trait_ref); + debug!("ast_path_to_poly_trait_ref({:?}, projections={:?}) -> {:?}", + trait_ref, poly_projections, poly_trait_ref); poly_trait_ref } fn ast_path_to_mono_trait_ref(&self, - rscope: &RegionScope, span: Span, trait_def_id: DefId, self_ty: Ty<'tcx>, @@ -738,8 +496,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { -> ty::TraitRef<'tcx> { let (substs, assoc_bindings) = - self.create_substs_for_ast_trait_ref(rscope, - span, + self.create_substs_for_ast_trait_ref(span, trait_def_id, self_ty, trait_segment); @@ -748,7 +505,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } fn create_substs_for_ast_trait_ref(&self, - rscope: &RegionScope, span: Span, trait_def_id: DefId, self_ty: Ty<'tcx>, @@ -792,8 +548,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } } - self.create_substs_for_ast_path(rscope, - span, + self.create_substs_for_ast_path(span, trait_def_id, &trait_segment.parameters, Some(self_ty)) @@ -902,7 +657,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } fn ast_path_to_ty(&self, - rscope: &RegionScope, span: Span, did: DefId, item_segment: &hir::PathSegment) @@ -916,8 +670,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } }; - let substs = self.ast_path_substs_for_ty(rscope, - span, + let substs = self.ast_path_substs_for_ty(span, did, item_segment); @@ -938,32 +691,27 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { ty::ExistentialTraitRef::erase_self_ty(self.tcx(), trait_ref) } - fn trait_path_to_object_type(&self, - rscope: &RegionScope, - path_span: Span, - trait_def_id: DefId, - trait_path_ref_id: ast::NodeId, - trait_segment: &hir::PathSegment, - span: Span, - partitioned_bounds: PartitionedBounds) - -> Ty<'tcx> { + fn conv_object_ty_poly_trait_ref(&self, + span: Span, + trait_bounds: &[hir::PolyTraitRef], + lifetime: &hir::Lifetime) + -> Ty<'tcx> + { let tcx = self.tcx(); + if trait_bounds.is_empty() { + span_err!(tcx.sess, span, E0224, + "at least one non-builtin trait is required for an object type"); + return tcx.types.err; + } + let mut projection_bounds = vec![]; let dummy_self = tcx.mk_ty(TRAIT_OBJECT_DUMMY_SELF); - let principal = self.ast_path_to_poly_trait_ref(rscope, - path_span, - trait_def_id, + let principal = self.instantiate_poly_trait_ref(&trait_bounds[0], dummy_self, - trait_path_ref_id, - trait_segment, &mut projection_bounds); - let PartitionedBounds { trait_bounds, - region_bounds } = - partitioned_bounds; - - let (auto_traits, trait_bounds) = split_auto_traits(tcx, trait_bounds); + let (auto_traits, trait_bounds) = split_auto_traits(tcx, &trait_bounds[1..]); if !trait_bounds.is_empty() { let b = &trait_bounds[0]; @@ -1038,23 +786,23 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { v.sort_by(|a, b| a.cmp(tcx, b)); let existential_predicates = ty::Binder(tcx.mk_existential_predicates(v.into_iter())); - let region_bound = self.compute_object_lifetime_bound(span, - ®ion_bounds, - existential_predicates); - let region_bound = match region_bound { - Some(r) => r, - None => { - tcx.mk_region(match rscope.object_lifetime_default(span) { - Some(r) => r, - None => { - span_err!(self.tcx().sess, span, E0228, + // Explicitly specified region bound. Use that. + let region_bound = if !lifetime.is_elided() { + self.ast_region_to_region(lifetime, None) + } else { + self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| { + if tcx.named_region_map.defs.contains_key(&lifetime.id) { + self.ast_region_to_region(lifetime, None) + } else { + self.re_infer(span, None).unwrap_or_else(|| { + span_err!(tcx.sess, span, E0228, "the lifetime bound for this object type cannot be deduced \ from context; please supply an explicit bound"); - ty::ReStatic - } - }) - } + tcx.mk_region(ty::ReStatic) + }) + } + }) }; debug!("region_bound: {:?}", region_bound); @@ -1265,7 +1013,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } fn qpath_to_ty(&self, - rscope: &RegionScope, span: Span, opt_self_ty: Option>, trait_def_id: DefId, @@ -1290,8 +1037,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { debug!("qpath_to_ty: self_type={:?}", self_ty); - let trait_ref = self.ast_path_to_mono_trait_ref(rscope, - span, + let trait_ref = self.ast_path_to_mono_trait_ref(span, trait_def_id, self_ty, trait_segment); @@ -1301,41 +1047,10 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { self.projected_ty(span, trait_ref, item_segment.name) } - /// Convert a type supplied as value for a type argument from AST into our - /// our internal representation. This is the same as `ast_ty_to_ty` but that - /// it applies the object lifetime default. - /// - /// # Parameters - /// - /// * `this`, `rscope`: the surrounding context - /// * `def`: the type parameter being instantiated (if available) - /// * `region_substs`: a partial substitution consisting of - /// only the region type parameters being supplied to this type. - /// * `ast_ty`: the ast representation of the type being supplied - fn ast_ty_arg_to_ty(&self, - rscope: &RegionScope, - def: Option<&ty::TypeParameterDef<'tcx>>, - region_substs: &[Kind<'tcx>], - ast_ty: &hir::Ty) - -> Ty<'tcx> - { - let tcx = self.tcx(); - - if let Some(def) = def { - let object_lifetime_default = def.object_lifetime_default.subst(tcx, region_substs); - let rscope1 = &ObjectLifetimeDefaultRscope::new(rscope, object_lifetime_default); - self.ast_ty_to_ty(rscope1, ast_ty) - } else { - self.ast_ty_to_ty(rscope, ast_ty) - } - } - // Check a type Path and convert it to a Ty. pub fn def_to_ty(&self, - rscope: &RegionScope, opt_self_ty: Option>, path: &hir::Path, - path_id: ast::NodeId, permit_variants: bool) -> Ty<'tcx> { let tcx = self.tcx(); @@ -1345,33 +1060,17 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let span = path.span; match path.def { - Def::Trait(trait_def_id) => { - // N.B. this case overlaps somewhat with - // TyTraitObject, see that fn for details - - assert_eq!(opt_self_ty, None); - tcx.prohibit_type_params(path.segments.split_last().unwrap().1); - - self.trait_path_to_object_type(rscope, - span, - trait_def_id, - path_id, - path.segments.last().unwrap(), - span, - partition_bounds(&[])) - } Def::Enum(did) | Def::TyAlias(did) | Def::Struct(did) | Def::Union(did) => { assert_eq!(opt_self_ty, None); tcx.prohibit_type_params(path.segments.split_last().unwrap().1); - self.ast_path_to_ty(rscope, span, did, path.segments.last().unwrap()) + self.ast_path_to_ty(span, did, path.segments.last().unwrap()) } Def::Variant(did) if permit_variants => { // Convert "variant type" as if it were a real type. // The resulting `Ty` is type of the variant's enum for now. assert_eq!(opt_self_ty, None); tcx.prohibit_type_params(path.segments.split_last().unwrap().1); - self.ast_path_to_ty(rscope, - span, + self.ast_path_to_ty(span, tcx.parent_def_id(did).unwrap(), path.segments.last().unwrap()) } @@ -1429,8 +1128,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { Def::AssociatedTy(def_id) => { tcx.prohibit_type_params(&path.segments[..path.segments.len()-2]); let trait_did = tcx.parent_def_id(def_id).unwrap(); - self.qpath_to_ty(rscope, - span, + self.qpath_to_ty(span, opt_self_ty, trait_did, &path.segments[path.segments.len()-2], @@ -1450,7 +1148,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { /// Parses the programmer's textual representation of a type into our /// internal notion of a type. - pub fn ast_ty_to_ty(&self, rscope: &RegionScope, ast_ty: &hir::Ty) -> Ty<'tcx> { + pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty) -> Ty<'tcx> { debug!("ast_ty_to_ty(id={:?}, ast_ty={:?})", ast_ty.id, ast_ty); @@ -1463,40 +1161,29 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { let result_ty = match ast_ty.node { hir::TySlice(ref ty) => { - tcx.mk_slice(self.ast_ty_to_ty(rscope, &ty)) + tcx.mk_slice(self.ast_ty_to_ty(&ty)) } hir::TyPtr(ref mt) => { tcx.mk_ptr(ty::TypeAndMut { - ty: self.ast_ty_to_ty(rscope, &mt.ty), + ty: self.ast_ty_to_ty(&mt.ty), mutbl: mt.mutbl }) } hir::TyRptr(ref region, ref mt) => { - let r = self.opt_ast_region_to_region(rscope, ast_ty.span, region); + let r = self.ast_region_to_region(region, None); debug!("TyRef r={:?}", r); - let rscope1 = - &ObjectLifetimeDefaultRscope::new( - rscope, - ty::ObjectLifetimeDefault::Specific(r)); - let t = self.ast_ty_to_ty(rscope1, &mt.ty); + let t = self.ast_ty_to_ty(&mt.ty); tcx.mk_ref(r, ty::TypeAndMut {ty: t, mutbl: mt.mutbl}) } hir::TyNever => { tcx.types.never }, hir::TyTup(ref fields) => { - tcx.mk_tup(fields.iter().map(|t| self.ast_ty_to_ty(rscope, &t))) + tcx.mk_tup(fields.iter().map(|t| self.ast_ty_to_ty(&t))) } hir::TyBareFn(ref bf) => { require_c_abi_if_variadic(tcx, &bf.decl, bf.abi, ast_ty.span); - let anon_scope = rscope.anon_type_scope(); - let bare_fn_ty = self.ty_of_method_or_bare_fn(bf.unsafety, - bf.abi, - None, - &bf.decl, - None, - anon_scope, - anon_scope); + let bare_fn_ty = self.ty_of_fn(bf.unsafety, bf.abi, &bf.decl); // Find any late-bound regions declared in return type that do // not appear in the arguments. These are not wellformed. @@ -1537,22 +1224,60 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } tcx.mk_fn_ptr(bare_fn_ty) } - hir::TyTraitObject(ref bounds) => { - self.conv_object_ty_poly_trait_ref(rscope, ast_ty.span, bounds) + hir::TyTraitObject(ref bounds, ref lifetime) => { + self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime) } hir::TyImplTrait(ref bounds) => { use collect::{compute_bounds, SizedByDefault}; + // Figure out if we can allow an `impl Trait` here, by walking up + // to a `fn` or inherent `impl` method, going only through `Ty` + // or `TraitRef` nodes (as nothing else should be in types) and + // ensuring that we reach the `fn`/method signature's return type. + let mut node_id = ast_ty.id; + let fn_decl = loop { + let parent = tcx.hir.get_parent_node(node_id); + match tcx.hir.get(parent) { + hir::map::NodeItem(&hir::Item { + node: hir::ItemFn(ref fn_decl, ..), .. + }) => break Some(fn_decl), + + hir::map::NodeImplItem(&hir::ImplItem { + node: hir::ImplItemKind::Method(ref sig, _), .. + }) => { + match tcx.hir.expect_item(tcx.hir.get_parent(parent)).node { + hir::ItemImpl(.., None, _, _) => { + break Some(&sig.decl) + } + _ => break None + } + } + + hir::map::NodeTy(_) | hir::map::NodeTraitRef(_) => {} + + _ => break None + } + node_id = parent; + }; + let allow = fn_decl.map_or(false, |fd| { + match fd.output { + hir::DefaultReturn(_) => false, + hir::Return(ref ty) => ty.id == node_id + } + }); + // Create the anonymized type. - let def_id = tcx.hir.local_def_id(ast_ty.id); - if let Some(anon_scope) = rscope.anon_type_scope() { - let substs = anon_scope.fresh_substs(self, ast_ty.span); + if allow { + let def_id = tcx.hir.local_def_id(ast_ty.id); + if let Err(ErrorReported) = self.get_generics(ast_ty.span, def_id) { + return tcx.types.err; + } + let substs = Substs::identity_for_item(tcx, def_id); let ty = tcx.mk_anon(tcx.hir.local_def_id(ast_ty.id), substs); // Collect the bounds, i.e. the `A+B+'c` in `impl A+B+'c`. let bounds = compute_bounds(self, ty, bounds, SizedByDefault::Yes, - Some(anon_scope), ast_ty.span); let predicates = bounds.predicates(tcx, ty); let predicates = tcx.lift_to_global(&predicates).unwrap(); @@ -1572,13 +1297,13 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { hir::TyPath(hir::QPath::Resolved(ref maybe_qself, ref path)) => { debug!("ast_ty_to_ty: maybe_qself={:?} path={:?}", maybe_qself, path); let opt_self_ty = maybe_qself.as_ref().map(|qself| { - self.ast_ty_to_ty(rscope, qself) + self.ast_ty_to_ty(qself) }); - self.def_to_ty(rscope, opt_self_ty, path, ast_ty.id, false) + self.def_to_ty(opt_self_ty, path, false) } hir::TyPath(hir::QPath::TypeRelative(ref qself, ref segment)) => { debug!("ast_ty_to_ty: qself={:?} segment={:?}", qself, segment); - let ty = self.ast_ty_to_ty(rscope, qself); + let ty = self.ast_ty_to_ty(qself); let def = if let hir::TyPath(hir::QPath::Resolved(_, ref path)) = qself.node { path.def @@ -1589,7 +1314,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } hir::TyArray(ref ty, length) => { if let Ok(length) = eval_length(tcx.global_tcx(), length, "array length") { - tcx.mk_array(self.ast_ty_to_ty(rscope, &ty), length) + tcx.mk_array(self.ast_ty_to_ty(&ty), length) } else { self.tcx().types.err } @@ -1617,7 +1342,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } pub fn ty_of_arg(&self, - rscope: &RegionScope, ty: &hir::Ty, expected_ty: Option>) -> Ty<'tcx> @@ -1625,87 +1349,26 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { match ty.node { hir::TyInfer if expected_ty.is_some() => expected_ty.unwrap(), hir::TyInfer => self.ty_infer(ty.span), - _ => self.ast_ty_to_ty(rscope, ty), + _ => self.ast_ty_to_ty(ty), } } - pub fn ty_of_method(&self, - sig: &hir::MethodSig, - opt_self_value_ty: Option>, - body: Option, - anon_scope: Option) - -> &'tcx ty::BareFnTy<'tcx> { - self.ty_of_method_or_bare_fn(sig.unsafety, - sig.abi, - opt_self_value_ty, - &sig.decl, - body, - None, - anon_scope) - } - - pub fn ty_of_bare_fn(&self, - unsafety: hir::Unsafety, - abi: abi::Abi, - decl: &hir::FnDecl, - body: hir::BodyId, - anon_scope: Option) - -> &'tcx ty::BareFnTy<'tcx> { - self.ty_of_method_or_bare_fn(unsafety, abi, None, decl, Some(body), None, anon_scope) - } - - fn ty_of_method_or_bare_fn(&self, - unsafety: hir::Unsafety, - abi: abi::Abi, - opt_self_value_ty: Option>, - decl: &hir::FnDecl, - body: Option, - arg_anon_scope: Option, - ret_anon_scope: Option) - -> &'tcx ty::BareFnTy<'tcx> - { - debug!("ty_of_method_or_bare_fn"); - - // New region names that appear inside of the arguments of the function - // declaration are bound to that function type. - let rb = MaybeWithAnonTypes::new(BindingRscope::new(), arg_anon_scope); + pub fn ty_of_fn(&self, + unsafety: hir::Unsafety, + abi: abi::Abi, + decl: &hir::FnDecl) + -> &'tcx ty::BareFnTy<'tcx> { + debug!("ty_of_fn"); let input_tys: Vec = - decl.inputs.iter().map(|a| self.ty_of_arg(&rb, a, None)).collect(); - - let has_self = opt_self_value_ty.is_some(); - let explicit_self = opt_self_value_ty.map(|self_value_ty| { - ExplicitSelf::determine(self_value_ty, input_tys[0]) - }); - - let implied_output_region = match explicit_self { - // `implied_output_region` is the region that will be assumed for any - // region parameters in the return type. In accordance with the rules for - // lifetime elision, we can determine it in two ways. First (determined - // here), if self is by-reference, then the implied output region is the - // region of the self parameter. - Some(ExplicitSelf::ByReference(region, _)) => Ok(*region), - - // Second, if there was exactly one lifetime (either a substitution or a - // reference) in the arguments, then any anonymous regions in the output - // have that lifetime. - _ => { - let arg_tys = &input_tys[has_self as usize..]; - let arg_params = has_self as usize..input_tys.len(); - self.find_implied_output_region(arg_tys, body, arg_params) - - } - }; + decl.inputs.iter().map(|a| self.ty_of_arg(a, None)).collect(); let output_ty = match decl.output { - hir::Return(ref output) => - self.convert_ty_with_lifetime_elision(implied_output_region, - &output, - ret_anon_scope), + hir::Return(ref output) => self.ast_ty_to_ty(output), hir::DefaultReturn(..) => self.tcx().mk_nil(), }; - debug!("ty_of_method_or_bare_fn: output_ty={:?}", output_ty); + debug!("ty_of_fn: output_ty={:?}", output_ty); self.tcx().mk_bare_fn(ty::BareFnTy { unsafety: unsafety, @@ -1728,10 +1391,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { debug!("ty_of_closure(expected_sig={:?})", expected_sig); - // new region names that appear inside of the fn decl are bound to - // that function type - let rb = rscope::BindingRscope::new(); - let input_tys = decl.inputs.iter().enumerate().map(|(i, a)| { let expected_arg_ty = expected_sig.as_ref().and_then(|e| { // no guarantee that the correct number of expected args @@ -1742,7 +1401,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { None } }); - self.ty_of_arg(&rb, a, expected_arg_ty) + self.ty_of_arg(a, expected_arg_ty) }); let expected_ret_ty = expected_sig.as_ref().map(|e| e.output()); @@ -1758,7 +1417,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { expected_ret_ty.unwrap(), _ if is_infer => self.ty_infer(decl.output.span()), hir::Return(ref output) => - self.ast_ty_to_ty(&rb, &output), + self.ast_ty_to_ty(&output), hir::DefaultReturn(..) => bug!(), }; @@ -1771,33 +1430,6 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } } - fn conv_object_ty_poly_trait_ref(&self, - rscope: &RegionScope, - span: Span, - ast_bounds: &[hir::TyParamBound]) - -> Ty<'tcx> - { - let mut partitioned_bounds = partition_bounds(ast_bounds); - - let trait_bound = if !partitioned_bounds.trait_bounds.is_empty() { - partitioned_bounds.trait_bounds.remove(0) - } else { - span_err!(self.tcx().sess, span, E0224, - "at least one non-builtin trait is required for an object type"); - return self.tcx().types.err; - }; - - let trait_ref = &trait_bound.trait_ref; - let trait_def_id = self.trait_def_id(trait_ref); - self.trait_path_to_object_type(rscope, - trait_ref.path.span, - trait_def_id, - trait_ref.ref_id, - trait_ref.path.segments.last().unwrap(), - span, - partitioned_bounds) - } - /// Given the bounds on an object, determines what single region bound (if any) we can /// use to summarize this type. The basic idea is that we will use the bound the user /// provided, if they provided one, and otherwise search the supertypes of trait bounds @@ -1805,27 +1437,14 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { /// we return `None`. fn compute_object_lifetime_bound(&self, span: Span, - explicit_region_bounds: &[&hir::Lifetime], existential_predicates: ty::Binder<&'tcx ty::Slice>>) -> Option<&'tcx ty::Region> // if None, use the default { let tcx = self.tcx(); - debug!("compute_opt_region_bound(explicit_region_bounds={:?}, \ - existential_predicates={:?})", - explicit_region_bounds, + debug!("compute_opt_region_bound(existential_predicates={:?})", existential_predicates); - if explicit_region_bounds.len() > 1 { - span_err!(tcx.sess, explicit_region_bounds[1].span, E0226, - "only a single explicit lifetime bound is permitted"); - } - - if let Some(&r) = explicit_region_bounds.get(0) { - // Explicitly specified region bound. Use that. - return Some(ast_region_to_region(tcx, r)); - } - if let Some(principal) = existential_predicates.principal() { if let Err(ErrorReported) = self.ensure_super_predicates(span, principal.def_id()) { return Some(tcx.mk_region(ty::ReStatic)); @@ -1861,18 +1480,13 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } } -pub struct PartitionedBounds<'a> { - pub trait_bounds: Vec<&'a hir::PolyTraitRef>, - pub region_bounds: Vec<&'a hir::Lifetime>, -} - /// Divides a list of general trait bounds into two groups: builtin bounds (Sync/Send) and the /// remaining general trait bounds. fn split_auto_traits<'a, 'b, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - trait_bounds: Vec<&'b hir::PolyTraitRef>) + trait_bounds: &'b [hir::PolyTraitRef]) -> (Vec, Vec<&'b hir::PolyTraitRef>) { - let (auto_traits, trait_bounds): (Vec<_>, _) = trait_bounds.into_iter().partition(|bound| { + let (auto_traits, trait_bounds): (Vec<_>, _) = trait_bounds.iter().partition(|bound| { match bound.trait_ref.path.def { Def::Trait(trait_did) => { // Checks whether `trait_did` refers to one of the builtin @@ -1909,30 +1523,6 @@ fn split_auto_traits<'a, 'b, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, (auto_traits, trait_bounds) } -/// Divides a list of bounds from the AST into two groups: general trait bounds and region bounds -pub fn partition_bounds<'a, 'b, 'gcx, 'tcx>(ast_bounds: &'b [hir::TyParamBound]) - -> PartitionedBounds<'b> -{ - let mut region_bounds = Vec::new(); - let mut trait_bounds = Vec::new(); - for ast_bound in ast_bounds { - match *ast_bound { - hir::TraitTyParamBound(ref b, hir::TraitBoundModifier::None) => { - trait_bounds.push(b); - } - hir::TraitTyParamBound(_, hir::TraitBoundModifier::Maybe) => {} - hir::RegionTyParamBound(ref l) => { - region_bounds.push(l); - } - } - } - - PartitionedBounds { - trait_bounds: trait_bounds, - region_bounds: region_bounds, - } -} - fn check_type_argument_count(tcx: TyCtxt, span: Span, supplied: usize, ty_param_defs: &[ty::TypeParameterDef]) { let accepted = ty_param_defs.len(); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 20c4f8f95224d..c2f32c2b52bbe 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -80,7 +80,7 @@ pub use self::Expectation::*; pub use self::compare_method::{compare_impl_method, compare_const_impl}; use self::TupleArgumentsFlag::*; -use astconv::{AstConv, ast_region_to_region}; +use astconv::AstConv; use dep_graph::DepNode; use fmt_macros::{Parser, Piece, Position}; use hir::def::{Def, CtorKind}; @@ -97,7 +97,6 @@ use rustc::ty::adjustment; use rustc::ty::fold::{BottomUpFolder, TypeFoldable}; use rustc::ty::util::{Representability, IntTypeExt}; use require_c_abi_if_variadic; -use rscope::{ElisionFailureInfo, RegionScope}; use session::{Session, CompileResult}; use CrateCtxt; use TypeAndSubsts; @@ -1410,6 +1409,15 @@ impl<'a, 'gcx, 'tcx> AstConv<'gcx, 'tcx> for FnCtxt<'a, 'gcx, 'tcx> { Ok(r) } + fn re_infer(&self, span: Span, def: Option<&ty::RegionParameterDef>) + -> Option<&'tcx ty::Region> { + let v = match def { + Some(def) => infer::EarlyBoundRegion(span, def.name), + None => infer::MiscVariable(span) + }; + Some(self.next_region_var(v)) + } + fn ty_infer(&self, span: Span) -> Ty<'tcx> { self.next_ty_var(TypeVariableOrigin::TypeInference(span)) } @@ -1450,30 +1458,6 @@ impl<'a, 'gcx, 'tcx> AstConv<'gcx, 'tcx> for FnCtxt<'a, 'gcx, 'tcx> { } } -impl<'a, 'gcx, 'tcx> RegionScope for FnCtxt<'a, 'gcx, 'tcx> { - fn object_lifetime_default(&self, span: Span) -> Option { - Some(self.base_object_lifetime_default(span)) - } - - fn base_object_lifetime_default(&self, span: Span) -> ty::Region { - // RFC #599 specifies that object lifetime defaults take - // precedence over other defaults. But within a fn body we - // don't have a *default* region, rather we use inference to - // find the *correct* region, which is strictly more general - // (and anyway, within a fn body the right region may not even - // be something the user can write explicitly, since it might - // be some expression). - *self.next_region_var(infer::MiscVariable(span)) - } - - fn anon_regions(&self, span: Span, count: usize) - -> Result, Option>> { - Ok((0..count).map(|_| { - *self.next_region_var(infer::MiscVariable(span)) - }).collect()) - } -} - /// Controls whether the arguments are tupled. This is used for the call /// operator. /// @@ -1830,7 +1814,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } pub fn to_ty(&self, ast_t: &hir::Ty) -> Ty<'tcx> { - let t = AstConv::ast_ty_to_ty(self, self, ast_t); + let t = AstConv::ast_ty_to_ty(self, ast_t); self.register_wf_obligation(t, ast_t.span, traits::MiscObligation); t } @@ -3974,7 +3958,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { match *qpath { hir::QPath::Resolved(ref maybe_qself, ref path) => { let opt_self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself)); - let ty = AstConv::def_to_ty(self, self, opt_self_ty, path, node_id, true); + let ty = AstConv::def_to_ty(self, opt_self_ty, path, true); (path.def, ty) } hir::QPath::TypeRelative(ref qself, ref segment) => { @@ -4406,10 +4390,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { None => &[] }; - if let Some(ast_lifetime) = lifetimes.get(i) { - ast_region_to_region(self.tcx, ast_lifetime) + if let Some(lifetime) = lifetimes.get(i) { + AstConv::ast_region_to_region(self, lifetime, Some(def)) } else { - self.region_var_for_def(span, def) + self.re_infer(span, Some(def)).unwrap() } }, |def, substs| { let mut i = def.index as usize; diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 1fd03b33a7612..bbd0c8058151f 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -57,7 +57,7 @@ There are some shortcomings in this design: */ -use astconv::{AstConv, ast_region_to_region, Bounds, PartitionedBounds, partition_bounds}; +use astconv::{AstConv, Bounds}; use lint; use constrained_type_params as ctp; use middle::lang_items::SizedTraitLangItem; @@ -68,10 +68,9 @@ use rustc::ty::subst::Substs; use rustc::ty::{ToPredicate, ImplContainer, AssociatedItemContainer, TraitContainer}; use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt}; use rustc::ty::util::IntTypeExt; -use rscope::*; use rustc::dep_graph::DepNode; use util::common::{ErrorReported, MemoizationMap}; -use util::nodemap::{NodeMap, FxHashMap, FxHashSet}; +use util::nodemap::{NodeMap, FxHashMap}; use CrateCtxt; use rustc_const_math::ConstInt; @@ -373,8 +372,8 @@ impl<'a,'tcx> CrateCtxt<'a,'tcx> { } impl<'a,'tcx> ItemCtxt<'a,'tcx> { - fn to_ty(&self, rs: &RS, ast_ty: &hir::Ty) -> Ty<'tcx> { - AstConv::ast_ty_to_ty(self, rs, ast_ty) + fn to_ty(&self, ast_ty: &hir::Ty) -> Ty<'tcx> { + AstConv::ast_ty_to_ty(self, ast_ty) } } @@ -437,6 +436,11 @@ impl<'a, 'tcx> AstConv<'tcx, 'tcx> for ItemCtxt<'a, 'tcx> { None } + fn re_infer(&self, _span: Span, _def: Option<&ty::RegionParameterDef>) + -> Option<&'tcx ty::Region> { + None + } + fn ty_infer(&self, span: Span) -> Ty<'tcx> { struct_span_err!( self.tcx().sess, @@ -626,7 +630,7 @@ fn convert_field<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, field: &hir::StructField, ty_f: &'tcx ty::FieldDef) { - let tt = ccx.icx(struct_predicates).to_ty(&ExplicitRscope, &field.ty); + let tt = ccx.icx(struct_predicates).to_ty(&field.ty); ccx.tcx.item_types.borrow_mut().insert(ty_f.did, tt); let def_id = ccx.tcx.hir.local_def_id(field.id); @@ -636,11 +640,8 @@ fn convert_field<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, } fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, - container: AssociatedItemContainer, id: ast::NodeId, sig: &hir::MethodSig, - untransformed_rcvr_ty: Ty<'tcx>, - body: Option, rcvr_ty_predicates: &ty::GenericPredicates<'tcx>,) { let def_id = ccx.tcx.hir.local_def_id(id); let ty_generics = generics_of_def_id(ccx, def_id); @@ -648,18 +649,8 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let ty_generic_predicates = ty_generic_predicates(ccx, &sig.generics, ty_generics.parent, vec![], false); - let anon_scope = match container { - ImplContainer(_) => Some(AnonTypeScope::new(def_id)), - TraitContainer(_) => None - }; - let assoc_item = ccx.tcx.associated_item(def_id); - let self_value_ty = if assoc_item.method_has_self_argument { - Some(untransformed_rcvr_ty) - } else { - None - }; - let fty = AstConv::ty_of_method(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)), - sig, self_value_ty, body, anon_scope); + let fty = AstConv::ty_of_fn(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)), + sig.unsafety, sig.abi, &sig.decl); let substs = mk_item_substs(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)), ccx.tcx.hir.span(id), def_id); @@ -765,7 +756,6 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { hir::ItemDefaultImpl(_, ref ast_trait_ref) => { let trait_ref = AstConv::instantiate_mono_trait_ref(&ccx.icx(&()), - &ExplicitRscope, ast_trait_ref, tcx.mk_self_type()); @@ -787,12 +777,11 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { debug!("convert: impl_bounds={:?}", ty_predicates); - let selfty = ccx.icx(&ty_predicates).to_ty(&ExplicitRscope, &selfty); + let selfty = ccx.icx(&ty_predicates).to_ty(&selfty); tcx.item_types.borrow_mut().insert(def_id, selfty); let trait_ref = opt_trait_ref.as_ref().map(|ast_trait_ref| { AstConv::instantiate_mono_trait_ref(&ccx.icx(&ty_predicates), - &ExplicitRscope, ast_trait_ref, selfty) }); @@ -858,8 +847,7 @@ fn convert_trait_item(ccx: &CrateCtxt, trait_item: &hir::TraitItem) { hir::TraitItemKind::Const(ref ty, _) => { let const_def_id = ccx.tcx.hir.local_def_id(trait_item.id); generics_of_def_id(ccx, const_def_id); - let ty = ccx.icx(&trait_predicates) - .to_ty(&ExplicitRscope, &ty); + let ty = ccx.icx(&trait_predicates).to_ty(&ty); tcx.item_types.borrow_mut().insert(const_def_id, ty); convert_associated_const(ccx, TraitContainer(trait_def_id), trait_item.id, ty); @@ -870,20 +858,14 @@ fn convert_trait_item(ccx: &CrateCtxt, trait_item: &hir::TraitItem) { generics_of_def_id(ccx, type_def_id); let typ = opt_ty.as_ref().map({ - |ty| ccx.icx(&trait_predicates).to_ty(&ExplicitRscope, &ty) + |ty| ccx.icx(&trait_predicates).to_ty(&ty) }); convert_associated_type(ccx, TraitContainer(trait_def_id), trait_item.id, typ); } - hir::TraitItemKind::Method(ref sig, ref method) => { - let body = match *method { - hir::TraitMethod::Required(_) => None, - hir::TraitMethod::Provided(body) => Some(body) - }; - convert_method(ccx, TraitContainer(trait_def_id), - trait_item.id, sig, tcx.mk_self_type(), - body, &trait_predicates); + hir::TraitItemKind::Method(ref sig, _) => { + convert_method(ccx, trait_item.id, sig, &trait_predicates); } } } @@ -896,14 +878,12 @@ fn convert_impl_item(ccx: &CrateCtxt, impl_item: &hir::ImplItem) { let impl_def_id = tcx.hir.get_parent_did(impl_item.id); let impl_predicates = tcx.item_predicates(impl_def_id); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); - let impl_self_ty = tcx.item_type(impl_def_id); match impl_item.node { hir::ImplItemKind::Const(ref ty, _) => { let const_def_id = ccx.tcx.hir.local_def_id(impl_item.id); generics_of_def_id(ccx, const_def_id); - let ty = ccx.icx(&impl_predicates) - .to_ty(&ExplicitRscope, &ty); + let ty = ccx.icx(&impl_predicates).to_ty(&ty); tcx.item_types.borrow_mut().insert(const_def_id, ty); convert_associated_const(ccx, ImplContainer(impl_def_id), impl_item.id, ty); @@ -918,15 +898,13 @@ fn convert_impl_item(ccx: &CrateCtxt, impl_item: &hir::ImplItem) { "associated types are not allowed in inherent impls"); } - let typ = ccx.icx(&impl_predicates).to_ty(&ExplicitRscope, ty); + let typ = ccx.icx(&impl_predicates).to_ty(ty); convert_associated_type(ccx, ImplContainer(impl_def_id), impl_item.id, Some(typ)); } - hir::ImplItemKind::Method(ref sig, body) => { - convert_method(ccx, ImplContainer(impl_def_id), - impl_item.id, sig, impl_self_ty, - Some(body), &impl_predicates); + hir::ImplItemKind::Method(ref sig, _) => { + convert_method(ccx, impl_item.id, sig, &impl_predicates); } } } @@ -1197,7 +1175,6 @@ fn ensure_super_predicates_step(ccx: &CrateCtxt, self_param_ty, bounds, SizedByDefault::No, - None, item.span); let superbounds1 = superbounds1.predicates(tcx, self_param_ty); @@ -1334,7 +1311,6 @@ fn convert_trait_predicates<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, it: &hir::Item) assoc_ty, bounds, SizedByDefault::Yes, - None, trait_item.span); bounds.predicates(ccx.tcx, assoc_ty).into_iter() @@ -1429,7 +1405,6 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, def_id: tcx.hir.local_def_id(param_id), default_def_id: tcx.hir.local_def_id(parent), default: None, - object_lifetime_default: ty::ObjectLifetimeDefault::BaseDefault, pure_wrt_drop: false, }; tcx.ty_param_defs.borrow_mut().insert(param_id, def.clone()); @@ -1471,9 +1446,6 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, name: l.lifetime.name, index: own_start + i as u32, def_id: tcx.hir.local_def_id(l.lifetime.id), - bounds: l.bounds.iter().map(|l| { - ast_region_to_region(tcx, l) - }).collect(), pure_wrt_drop: l.pure_wrt_drop, } }).collect::>(); @@ -1482,7 +1454,7 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let type_start = own_start + regions.len() as u32; let types = ast_generics.ty_params.iter().enumerate().map(|(i, p)| { let i = type_start + i as u32; - get_or_create_type_parameter_def(ccx, ast_generics, i, p, allow_defaults) + get_or_create_type_parameter_def(ccx, i, p, allow_defaults) }); let mut types: Vec<_> = opt_self.into_iter().chain(types).collect(); @@ -1497,24 +1469,11 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, def_id: def_id, default_def_id: parent_def_id.unwrap(), default: None, - object_lifetime_default: ty::ObjectLifetimeDefault::BaseDefault, pure_wrt_drop: false, })); }); } - // Debugging aid. - if tcx.has_attr(def_id, "rustc_object_lifetime_default") { - let object_lifetime_default_reprs: String = - types.iter().map(|t| { - match t.object_lifetime_default { - ty::ObjectLifetimeDefault::Specific(r) => r.to_string(), - d => format!("{:?}", d), - } - }).collect::>().join(","); - tcx.sess.span_err(tcx.hir.span(node_id), &object_lifetime_default_reprs); - } - tcx.alloc_generics(ty::Generics { parent: parent_def_id, parent_regions: parent_regions, @@ -1545,16 +1504,15 @@ fn type_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, NodeItem(item) => { match item.node { ItemStatic(ref t, ..) | ItemConst(ref t, _) => { - ccx.icx(&()).to_ty(&StaticRscope::new(&ccx.tcx), &t) + ccx.icx(&()).to_ty(&t) } - ItemFn(ref decl, unsafety, _, abi, ref generics, body) => { - let tofd = AstConv::ty_of_bare_fn(&ccx.icx(generics), unsafety, abi, &decl, - body, Some(AnonTypeScope::new(def_id))); + ItemFn(ref decl, unsafety, _, abi, ref generics, _) => { + let tofd = AstConv::ty_of_fn(&ccx.icx(generics), unsafety, abi, &decl); let substs = mk_item_substs(&ccx.icx(generics), item.span, def_id); ccx.tcx.mk_fn_def(def_id, substs, tofd) } ItemTy(ref t, ref generics) => { - ccx.icx(generics).to_ty(&ExplicitRscope, &t) + ccx.icx(generics).to_ty(&t) } ItemEnum(ref ei, ref generics) => { let def = convert_enum_def(ccx, item, ei); @@ -1595,7 +1553,7 @@ fn type_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, fn_decl, generics, abi) } ForeignItemStatic(ref t, _) => { - ccx.icx(&()).to_ty(&ExplicitRscope, t) + ccx.icx(&()).to_ty(t) } } } @@ -1765,7 +1723,7 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, name: param.lifetime.name })); for bound in ¶m.bounds { - let bound_region = ast_region_to_region(ccx.tcx, bound); + let bound_region = AstConv::ast_region_to_region(&ccx.icx(&()), bound, None); let outlives = ty::Binder(ty::OutlivesPredicate(region, bound_region)); predicates.push(outlives.to_predicate()); } @@ -1781,7 +1739,6 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, param_ty, ¶m.bounds, SizedByDefault::Yes, - None, param.span); predicates.extend(bounds.predicates(ccx.tcx, param_ty)); } @@ -1792,7 +1749,6 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, match predicate { &hir::WherePredicate::BoundPredicate(ref bound_pred) => { let ty = AstConv::ast_ty_to_ty(&ccx.icx(&(base_predicates, ast_generics)), - &ExplicitRscope, &bound_pred.bounded_ty); for bound in bound_pred.bounds.iter() { @@ -1803,7 +1759,6 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, let trait_ref = AstConv::instantiate_poly_trait_ref(&ccx.icx(&(base_predicates, ast_generics)), - &ExplicitRscope, poly_trait_ref, ty, &mut projections); @@ -1816,7 +1771,9 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, } &hir::TyParamBound::RegionTyParamBound(ref lifetime) => { - let region = ast_region_to_region(tcx, lifetime); + let region = AstConv::ast_region_to_region(&ccx.icx(&()), + lifetime, + None); let pred = ty::Binder(ty::OutlivesPredicate(ty, region)); predicates.push(ty::Predicate::TypeOutlives(pred)) } @@ -1825,9 +1782,9 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, } &hir::WherePredicate::RegionPredicate(ref region_pred) => { - let r1 = ast_region_to_region(tcx, ®ion_pred.lifetime); + let r1 = AstConv::ast_region_to_region(&ccx.icx(&()), ®ion_pred.lifetime, None); for bound in ®ion_pred.bounds { - let r2 = ast_region_to_region(tcx, bound); + let r2 = AstConv::ast_region_to_region(&ccx.icx(&()), bound, None); let pred = ty::Binder(ty::OutlivesPredicate(r1, r2)); predicates.push(ty::Predicate::RegionOutlives(pred)) } @@ -1846,7 +1803,6 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, } fn get_or_create_type_parameter_def<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, - ast_generics: &hir::Generics, index: u32, param: &hir::TyParam, allow_defaults: bool) @@ -1859,11 +1815,7 @@ fn get_or_create_type_parameter_def<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, } let default = - param.default.as_ref().map(|def| ccx.icx(&()).to_ty(&ExplicitRscope, def)); - - let object_lifetime_default = - compute_object_lifetime_default(ccx, param.id, - ¶m.bounds, &ast_generics.where_clause); + param.default.as_ref().map(|def| ccx.icx(&()).to_ty(def)); let parent = tcx.hir.get_parent(param.id); @@ -1884,7 +1836,6 @@ fn get_or_create_type_parameter_def<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, def_id: ccx.tcx.hir.local_def_id(param.id), default_def_id: ccx.tcx.hir.local_def_id(parent), default: default, - object_lifetime_default: object_lifetime_default, pure_wrt_drop: param.pure_wrt_drop, }; @@ -1899,75 +1850,6 @@ fn get_or_create_type_parameter_def<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, def } -/// Scan the bounds and where-clauses on a parameter to extract bounds -/// of the form `T:'a` so as to determine the `ObjectLifetimeDefault`. -/// This runs as part of computing the minimal type scheme, so we -/// intentionally avoid just asking astconv to convert all the where -/// clauses into a `ty::Predicate`. This is because that could induce -/// artificial cycles. -fn compute_object_lifetime_default<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, - param_id: ast::NodeId, - param_bounds: &[hir::TyParamBound], - where_clause: &hir::WhereClause) - -> ty::ObjectLifetimeDefault<'tcx> -{ - let inline_bounds = from_bounds(ccx, param_bounds); - let where_bounds = from_predicates(ccx, param_id, &where_clause.predicates); - let all_bounds: FxHashSet<_> = inline_bounds.into_iter() - .chain(where_bounds) - .collect(); - return if all_bounds.len() > 1 { - ty::ObjectLifetimeDefault::Ambiguous - } else if all_bounds.len() == 0 { - ty::ObjectLifetimeDefault::BaseDefault - } else { - ty::ObjectLifetimeDefault::Specific( - all_bounds.into_iter().next().unwrap()) - }; - - fn from_bounds<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, - bounds: &[hir::TyParamBound]) - -> Vec<&'tcx ty::Region> - { - bounds.iter() - .filter_map(|bound| { - match *bound { - hir::TraitTyParamBound(..) => - None, - hir::RegionTyParamBound(ref lifetime) => - Some(ast_region_to_region(ccx.tcx, lifetime)), - } - }) - .collect() - } - - fn from_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, - param_id: ast::NodeId, - predicates: &[hir::WherePredicate]) - -> Vec<&'tcx ty::Region> - { - predicates.iter() - .flat_map(|predicate| { - match *predicate { - hir::WherePredicate::BoundPredicate(ref data) => { - if data.bound_lifetimes.is_empty() && - is_param(ccx.tcx, &data.bounded_ty, param_id) - { - from_bounds(ccx, &data.bounds).into_iter() - } else { - Vec::new().into_iter() - } - } - hir::WherePredicate::RegionPredicate(..) | - hir::WherePredicate::EqPredicate(..) => { - Vec::new().into_iter() - } - } - }) - .collect() - } -} - pub enum SizedByDefault { Yes, No, } /// Translate the AST's notion of ty param bounds (which are an enum consisting of a newtyped Ty or @@ -1977,28 +1859,33 @@ pub fn compute_bounds<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>, param_ty: ty::Ty<'tcx>, ast_bounds: &[hir::TyParamBound], sized_by_default: SizedByDefault, - anon_scope: Option, span: Span) -> Bounds<'tcx> { - let tcx = astconv.tcx(); - let PartitionedBounds { - trait_bounds, - region_bounds - } = partition_bounds(&ast_bounds); + let mut region_bounds = vec![]; + let mut trait_bounds = vec![]; + for ast_bound in ast_bounds { + match *ast_bound { + hir::TraitTyParamBound(ref b, hir::TraitBoundModifier::None) => { + trait_bounds.push(b); + } + hir::TraitTyParamBound(_, hir::TraitBoundModifier::Maybe) => {} + hir::RegionTyParamBound(ref l) => { + region_bounds.push(l); + } + } + } let mut projection_bounds = vec![]; - let rscope = MaybeWithAnonTypes::new(ExplicitRscope, anon_scope); let mut trait_bounds: Vec<_> = trait_bounds.iter().map(|&bound| { - astconv.instantiate_poly_trait_ref(&rscope, - bound, + astconv.instantiate_poly_trait_ref(bound, param_ty, &mut projection_bounds) }).collect(); let region_bounds = region_bounds.into_iter().map(|r| { - ast_region_to_region(tcx, r) + astconv.ast_region_to_region(r, None) }).collect(); trait_bounds.sort_by(|a,b| a.def_id().cmp(&b.def_id())); @@ -2030,8 +1917,7 @@ fn predicates_from_bound<'tcx>(astconv: &AstConv<'tcx, 'tcx>, match *bound { hir::TraitTyParamBound(ref tr, hir::TraitBoundModifier::None) => { let mut projections = Vec::new(); - let pred = astconv.instantiate_poly_trait_ref(&ExplicitRscope, - tr, + let pred = astconv.instantiate_poly_trait_ref(tr, param_ty, &mut projections); projections.into_iter() @@ -2040,7 +1926,7 @@ fn predicates_from_bound<'tcx>(astconv: &AstConv<'tcx, 'tcx>, .collect() } hir::RegionTyParamBound(ref lifetime) => { - let region = ast_region_to_region(astconv.tcx(), lifetime); + let region = astconv.ast_region_to_region(lifetime, None); let pred = ty::Binder(ty::OutlivesPredicate(param_ty, region)); vec![ty::Predicate::TypeOutlives(pred)] } @@ -2058,18 +1944,7 @@ fn compute_type_of_foreign_fn_decl<'a, 'tcx>( abi: abi::Abi) -> Ty<'tcx> { - let rb = BindingRscope::new(); - let input_tys = decl.inputs - .iter() - .map(|a| AstConv::ty_of_arg(&ccx.icx(ast_generics), &rb, a, None)) - .collect::>(); - - let output = match decl.output { - hir::Return(ref ty) => - AstConv::ast_ty_to_ty(&ccx.icx(ast_generics), &rb, &ty), - hir::DefaultReturn(..) => - ccx.tcx.mk_nil(), - }; + let fty = AstConv::ty_of_fn(&ccx.icx(ast_generics), hir::Unsafety::Unsafe, abi, decl); // feature gate SIMD types in FFI, since I (huonw) am not sure the // ABIs are handled at all correctly. @@ -2085,27 +1960,23 @@ fn compute_type_of_foreign_fn_decl<'a, 'tcx>( .emit(); } }; - for (input, ty) in decl.inputs.iter().zip(&input_tys) { + for (input, ty) in decl.inputs.iter().zip(*fty.sig.inputs().skip_binder()) { check(&input, ty) } if let hir::Return(ref ty) = decl.output { - check(&ty, output) + check(&ty, *fty.sig.output().skip_binder()) } } let id = ccx.tcx.hir.as_local_node_id(def_id).unwrap(); let substs = mk_item_substs(&ccx.icx(ast_generics), ccx.tcx.hir.span(id), def_id); - ccx.tcx.mk_fn_def(def_id, substs, ccx.tcx.mk_bare_fn(ty::BareFnTy { - abi: abi, - unsafety: hir::Unsafety::Unsafe, - sig: ty::Binder(ccx.tcx.mk_fn_sig(input_tys.into_iter(), output, decl.variadic)), - })) + ccx.tcx.mk_fn_def(def_id, substs, fty) } -pub fn mk_item_substs<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>, - span: Span, - def_id: DefId) - -> &'tcx Substs<'tcx> { +fn mk_item_substs<'tcx>(astconv: &AstConv<'tcx, 'tcx>, + span: Span, + def_id: DefId) + -> &'tcx Substs<'tcx> { let tcx = astconv.tcx(); // FIXME(eddyb) Do this request from Substs::for_item in librustc. if let Err(ErrorReported) = astconv.get_generics(span, def_id) { diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 6d943f3ca2ef6..c41d40b41e42a 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -1412,85 +1412,19 @@ fn main() { ``` "##, -E0106: r##" -This error indicates that a lifetime is missing from a type. If it is an error -inside a function signature, the problem may be with failing to adhere to the -lifetime elision rules (see below). - -Here are some simple examples of where you'll run into this error: - -```compile_fail,E0106 -struct Foo { x: &bool } // error -struct Foo<'a> { x: &'a bool } // correct - -enum Bar { A(u8), B(&bool), } // error -enum Bar<'a> { A(u8), B(&'a bool), } // correct - -type MyStr = &str; // error -type MyStr<'a> = &'a str; // correct -``` - -Lifetime elision is a special, limited kind of inference for lifetimes in -function signatures which allows you to leave out lifetimes in certain cases. -For more background on lifetime elision see [the book][book-le]. - -The lifetime elision rules require that any function signature with an elided -output lifetime must either have - - - exactly one input lifetime - - or, multiple input lifetimes, but the function must also be a method with a - `&self` or `&mut self` receiver - -In the first case, the output lifetime is inferred to be the same as the unique -input lifetime. In the second case, the lifetime is instead inferred to be the -same as the lifetime on `&self` or `&mut self`. - -Here are some examples of elision errors: - -```compile_fail,E0106 -// error, no input lifetimes -fn foo() -> &str { } - -// error, `x` and `y` have distinct lifetimes inferred -fn bar(x: &str, y: &str) -> &str { } - -// error, `y`'s lifetime is inferred to be distinct from `x`'s -fn baz<'a>(x: &'a str, y: &str) -> &str { } -``` - -[book-le]: https://doc.rust-lang.org/nightly/book/lifetimes.html#lifetime-elision -"##, - E0107: r##" This error means that an incorrect number of lifetime parameters were provided -for a type (like a struct or enum) or trait. - -Some basic examples include: +for a type (like a struct or enum) or trait: ```compile_fail,E0107 -struct Foo<'a>(&'a str); +struct Foo<'a, 'b>(&'a str, &'b str); enum Bar { A, B, C } struct Baz<'a> { - foo: Foo, // error: expected 1, found 0 + foo: Foo<'a>, // error: expected 2, found 1 bar: Bar<'a>, // error: expected 0, found 1 } ``` - -Here's an example that is currently an error, but may work in a future version -of Rust: - -```compile_fail,E0107 -struct Foo<'a>(&'a str); - -trait Quux { } -impl Quux for Foo { } // error: expected 1, found 0 -``` - -Lifetime elision in implementation headers was part of the lifetime elision -RFC. It is, however, [currently unimplemented][iss15872]. - -[iss15872]: https://github.com/rust-lang/rust/issues/15872 "##, E0116: r##" @@ -4162,7 +4096,6 @@ register_diagnostics! { // E0222, // Error code E0045 (variadic function must have C calling // convention) duplicate E0224, // at least one non-builtin train is required for an object type - E0226, // only a single explicit lifetime bound is permitted E0227, // ambiguous lifetime bound, explicit lifetime bound required E0228, // explicit lifetime bound required E0231, // only named substitution parameters are allowed diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 0e7daa03404c5..4ed116b88f6d9 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -77,6 +77,7 @@ This API is completely unstable and subject to change. #![feature(box_patterns)] #![feature(box_syntax)] #![feature(conservative_impl_trait)] +#![feature(loop_break_value)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] @@ -126,7 +127,6 @@ pub mod diagnostics; pub mod check; pub mod check_unused; -mod rscope; mod astconv; pub mod collect; mod constrained_type_params; diff --git a/src/librustc_typeck/rscope.rs b/src/librustc_typeck/rscope.rs deleted file mode 100644 index 3f5e443a20a68..0000000000000 --- a/src/librustc_typeck/rscope.rs +++ /dev/null @@ -1,393 +0,0 @@ -// Copyright 2012 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. - -use rustc::hir; -use rustc::hir::def_id::DefId; -use rustc::ty; -use rustc::ty::subst::Substs; - -use astconv::AstConv; - -use std::cell::Cell; -use syntax_pos::Span; - -#[derive(Clone)] -pub struct ElisionFailureInfo { - /// Where we can find the argument pattern. - pub parent: Option, - /// The index of the argument in the original definition. - pub index: usize, - pub lifetime_count: usize, - pub have_bound_regions: bool -} - -pub type ElidedLifetime = Result>>; - -/// Defines strategies for handling regions that are omitted. For -/// example, if one writes the type `&Foo`, then the lifetime of -/// this reference has been omitted. When converting this -/// type, the generic functions in astconv will invoke `anon_regions` -/// on the provided region-scope to decide how to translate this -/// omitted region. -/// -/// It is not always legal to omit regions, therefore `anon_regions` -/// can return `Err(())` to indicate that this is not a scope in which -/// regions can legally be omitted. -pub trait RegionScope { - fn anon_regions(&self, - span: Span, - count: usize) - -> Result, Option>>; - - /// If an object omits any explicit lifetime bound, and none can - /// be derived from the object traits, what should we use? If - /// `None` is returned, an explicit annotation is required. - fn object_lifetime_default(&self, span: Span) -> Option; - - /// The "base" default is the initial default for a scope. This is - /// 'static except for in fn bodies, where it is a fresh inference - /// variable. You shouldn't call this except for as part of - /// computing `object_lifetime_default` (in particular, in legacy - /// modes, it may not be relevant). - fn base_object_lifetime_default(&self, span: Span) -> ty::Region; - - /// If this scope allows anonymized types, return the generics in - /// scope, that anonymized types will close over. For example, - /// if you have a function like: - /// - /// fn foo<'a, T>() -> impl Trait { ... } - /// - /// then, for the rscope that is used when handling the return type, - /// `anon_type_scope()` would return a `Some(AnonTypeScope {...})`, - /// on which `.fresh_substs(...)` can be used to obtain identity - /// Substs for `'a` and `T`, to track them in `TyAnon`. This property - /// is controlled by the region scope because it's fine-grained enough - /// to allow restriction of anonymized types to the syntactical extent - /// of a function's return type. - fn anon_type_scope(&self) -> Option { - None - } -} - -#[derive(Copy, Clone)] -pub struct AnonTypeScope { - enclosing_item: DefId -} - -impl<'gcx: 'tcx, 'tcx> AnonTypeScope { - pub fn new(enclosing_item: DefId) -> AnonTypeScope { - AnonTypeScope { - enclosing_item: enclosing_item - } - } - - pub fn fresh_substs(&self, astconv: &AstConv<'gcx, 'tcx>, span: Span) - -> &'tcx Substs<'tcx> { - use collect::mk_item_substs; - - mk_item_substs(astconv, span, self.enclosing_item) - } -} - -/// A scope wrapper which optionally allows anonymized types. -#[derive(Copy, Clone)] -pub struct MaybeWithAnonTypes { - base_scope: R, - anon_scope: Option -} - -impl MaybeWithAnonTypes { - pub fn new(base_scope: R, anon_scope: Option) -> Self { - MaybeWithAnonTypes { - base_scope: base_scope, - anon_scope: anon_scope - } - } -} - -impl RegionScope for MaybeWithAnonTypes { - fn object_lifetime_default(&self, span: Span) -> Option { - self.base_scope.object_lifetime_default(span) - } - - fn anon_regions(&self, - span: Span, - count: usize) - -> Result, Option>> { - self.base_scope.anon_regions(span, count) - } - - fn base_object_lifetime_default(&self, span: Span) -> ty::Region { - self.base_scope.base_object_lifetime_default(span) - } - - fn anon_type_scope(&self) -> Option { - self.anon_scope - } -} - -// A scope in which all regions must be explicitly named. This is used -// for types that appear in structs and so on. -#[derive(Copy, Clone)] -pub struct ExplicitRscope; - -impl RegionScope for ExplicitRscope { - fn anon_regions(&self, - _span: Span, - _count: usize) - -> Result, Option>> { - Err(None) - } - - fn object_lifetime_default(&self, span: Span) -> Option { - Some(self.base_object_lifetime_default(span)) - } - - fn base_object_lifetime_default(&self, _span: Span) -> ty::Region { - ty::ReStatic - } -} - -// Same as `ExplicitRscope`, but provides some extra information for diagnostics -pub struct UnelidableRscope(Option>); - -impl UnelidableRscope { - pub fn new(v: Option>) -> UnelidableRscope { - UnelidableRscope(v) - } -} - -impl RegionScope for UnelidableRscope { - fn anon_regions(&self, - _span: Span, - _count: usize) - -> Result, Option>> { - let UnelidableRscope(ref v) = *self; - Err(v.clone()) - } - - fn object_lifetime_default(&self, span: Span) -> Option { - Some(self.base_object_lifetime_default(span)) - } - - fn base_object_lifetime_default(&self, _span: Span) -> ty::Region { - ty::ReStatic - } -} - -// A scope in which omitted anonymous region defaults to -// `default`. This is used after the `->` in function signatures. The -// latter use may go away. Note that object-lifetime defaults work a -// bit differently, as specified in RFC #599. -pub struct ElidableRscope { - default: ty::Region, -} - -impl ElidableRscope { - pub fn new(r: ty::Region) -> ElidableRscope { - ElidableRscope { default: r } - } -} - -impl RegionScope for ElidableRscope { - fn object_lifetime_default(&self, span: Span) -> Option { - // Per RFC #599, object-lifetimes default to 'static unless - // overridden by context, and this takes precedence over - // lifetime elision. - Some(self.base_object_lifetime_default(span)) - } - - fn base_object_lifetime_default(&self, _span: Span) -> ty::Region { - ty::ReStatic - } - - fn anon_regions(&self, - _span: Span, - count: usize) - -> Result, Option>> - { - Ok(vec![self.default; count]) - } -} - -/// A scope that behaves as an ElidabeRscope with a `'static` default region -/// that should also warn if the `static_in_const` feature is unset. -#[derive(Copy, Clone)] -pub struct StaticRscope<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { - tcx: &'a ty::TyCtxt<'a, 'gcx, 'tcx>, -} - -impl<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> StaticRscope<'a, 'gcx, 'tcx> { - /// create a new StaticRscope from a reference to the `TyCtxt` - pub fn new(tcx: &'a ty::TyCtxt<'a, 'gcx, 'tcx>) -> Self { - StaticRscope { tcx: tcx } - } -} - -impl<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> RegionScope for StaticRscope<'a, 'gcx, 'tcx> { - fn anon_regions(&self, - span: Span, - count: usize) - -> Result, Option>> { - if !self.tcx.sess.features.borrow().static_in_const { - self.tcx - .sess - .struct_span_err(span, - "this needs a `'static` lifetime or the \ - `static_in_const` feature, see #35897") - .emit(); - } - Ok(vec![ty::ReStatic; count]) - } - - fn object_lifetime_default(&self, span: Span) -> Option { - Some(self.base_object_lifetime_default(span)) - } - - fn base_object_lifetime_default(&self, _span: Span) -> ty::Region { - ty::ReStatic - } -} - -/// A scope in which we generate anonymous, late-bound regions for -/// omitted regions. This occurs in function signatures. -pub struct BindingRscope { - anon_bindings: Cell, -} - -impl BindingRscope { - pub fn new() -> BindingRscope { - BindingRscope { - anon_bindings: Cell::new(0), - } - } - - fn next_region(&self) -> ty::Region { - let idx = self.anon_bindings.get(); - self.anon_bindings.set(idx + 1); - ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrAnon(idx)) - } -} - -impl RegionScope for BindingRscope { - fn object_lifetime_default(&self, span: Span) -> Option { - // Per RFC #599, object-lifetimes default to 'static unless - // overridden by context, and this takes precedence over the - // binding defaults in a fn signature. - Some(self.base_object_lifetime_default(span)) - } - - fn base_object_lifetime_default(&self, _span: Span) -> ty::Region { - ty::ReStatic - } - - fn anon_regions(&self, - _: Span, - count: usize) - -> Result, Option>> - { - Ok((0..count).map(|_| self.next_region()).collect()) - } -} - -/// A scope which overrides the default object lifetime but has no other effect. -pub struct ObjectLifetimeDefaultRscope<'r> { - base_scope: &'r (RegionScope+'r), - default: ty::ObjectLifetimeDefault<'r>, -} - -impl<'r> ObjectLifetimeDefaultRscope<'r> { - pub fn new(base_scope: &'r (RegionScope+'r), - default: ty::ObjectLifetimeDefault<'r>) - -> ObjectLifetimeDefaultRscope<'r> - { - ObjectLifetimeDefaultRscope { - base_scope: base_scope, - default: default, - } - } -} - -impl<'r> RegionScope for ObjectLifetimeDefaultRscope<'r> { - fn object_lifetime_default(&self, span: Span) -> Option { - match self.default { - ty::ObjectLifetimeDefault::Ambiguous => - None, - - ty::ObjectLifetimeDefault::BaseDefault => - // NB: This behavior changed in Rust 1.3. - Some(self.base_object_lifetime_default(span)), - - ty::ObjectLifetimeDefault::Specific(r) => - Some(*r), - } - } - - fn base_object_lifetime_default(&self, span: Span) -> ty::Region { - self.base_scope.base_object_lifetime_default(span) - } - - fn anon_regions(&self, - span: Span, - count: usize) - -> Result, Option>> - { - self.base_scope.anon_regions(span, count) - } - - fn anon_type_scope(&self) -> Option { - self.base_scope.anon_type_scope() - } -} - -/// A scope which simply shifts the Debruijn index of other scopes -/// to account for binding levels. -pub struct ShiftedRscope<'r> { - base_scope: &'r (RegionScope+'r) -} - -impl<'r> ShiftedRscope<'r> { - pub fn new(base_scope: &'r (RegionScope+'r)) -> ShiftedRscope<'r> { - ShiftedRscope { base_scope: base_scope } - } -} - -impl<'r> RegionScope for ShiftedRscope<'r> { - fn object_lifetime_default(&self, span: Span) -> Option { - self.base_scope.object_lifetime_default(span) - .map(|r| ty::fold::shift_region(r, 1)) - } - - fn base_object_lifetime_default(&self, span: Span) -> ty::Region { - ty::fold::shift_region(self.base_scope.base_object_lifetime_default(span), 1) - } - - fn anon_regions(&self, - span: Span, - count: usize) - -> Result, Option>> - { - match self.base_scope.anon_regions(span, count) { - Ok(mut v) => { - for r in &mut v { - *r = ty::fold::shift_region(*r, 1); - } - Ok(v) - } - Err(errs) => { - Err(errs) - } - } - } - - fn anon_type_scope(&self) -> Option { - self.base_scope.anon_type_scope() - } -} diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index 55033330a4490..ba00f237684e6 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -154,7 +154,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { let tcx = self.terms_cx.tcx; assert!(is_lifetime(&tcx.hir, param_id)); match tcx.named_region_map.defs.get(¶m_id) { - Some(&rl::DefEarlyBoundRegion(_, lifetime_decl_id)) => lifetime_decl_id, + Some(&rl::Region::EarlyBound(_, lifetime_decl_id)) => lifetime_decl_id, Some(_) => bug!("should not encounter non early-bound cases"), // The lookup should only fail when `param_id` is diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 080bc5072d69b..7591475c5d3fa 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -28,7 +28,7 @@ use syntax::symbol::keywords; use syntax_pos::{self, DUMMY_SP, Pos}; use rustc::middle::privacy::AccessLevels; -use rustc::middle::resolve_lifetime::DefRegion::*; +use rustc::middle::resolve_lifetime as rl; use rustc::middle::lang_items; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; @@ -765,9 +765,9 @@ impl Clean for hir::Lifetime { fn clean(&self, cx: &DocContext) -> Lifetime { let def = cx.tcx.named_region_map.defs.get(&self.id).cloned(); match def { - Some(DefEarlyBoundRegion(_, node_id)) | - Some(DefLateBoundRegion(_, node_id)) | - Some(DefFreeRegion(_, 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; } @@ -794,7 +794,7 @@ impl Clean for hir::LifetimeDef { } } -impl<'tcx> Clean for ty::RegionParameterDef<'tcx> { +impl Clean for ty::RegionParameterDef { fn clean(&self, _: &DocContext) -> Lifetime { Lifetime(self.name.to_string()) } @@ -970,11 +970,6 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics<'tcx>, Some(tp.clean(cx)) } }).collect::>(); - let stripped_lifetimes = gens.regions.iter().map(|rp| { - let mut srp = rp.clone(); - srp.bounds = Vec::new(); - srp.clean(cx) - }).collect::>(); let mut where_predicates = preds.predicates.to_vec().clean(cx); @@ -1017,7 +1012,7 @@ impl<'a, 'tcx> Clean for (&'a ty::Generics<'tcx>, Generics { type_params: simplify::ty_params(stripped_typarams), - lifetimes: stripped_lifetimes, + lifetimes: gens.regions.clean(cx), where_predicates: simplify::where_clauses(cx, where_predicates), } } @@ -1688,9 +1683,15 @@ impl Clean for hir::Ty { match self.node { TyNever => Never, TyPtr(ref m) => RawPointer(m.mutbl.clean(cx), box m.ty.clean(cx)), - TyRptr(ref l, ref m) => - BorrowedRef {lifetime: l.clean(cx), mutability: m.mutbl.clean(cx), - type_: box m.ty.clean(cx)}, + TyRptr(ref l, ref m) => { + let lifetime = if l.is_elided() { + None + } else { + Some(l.clean(cx)) + }; + BorrowedRef {lifetime: lifetime, mutability: m.mutbl.clean(cx), + type_: box m.ty.clean(cx)} + } TySlice(ref ty) => Vector(box ty.clean(cx)), TyArray(ref ty, length) => { use rustc_const_eval::eval_length; @@ -1729,7 +1730,9 @@ impl Clean for hir::Ty { for (i, lt_param) in generics.lifetimes.iter().enumerate() { if let Some(lt) = provided_params.lifetimes().get(i).cloned() .cloned() { - lt_substs.insert(lt_param.lifetime.id, lt.clean(cx)); + if !lt.is_elided() { + lt_substs.insert(lt_param.lifetime.id, lt.clean(cx)); + } } } return cx.enter_alias(ty_substs, lt_substs, || ty.clean(cx)); @@ -1768,20 +1771,20 @@ impl Clean for hir::Ty { trait_: box resolve_type(cx, trait_path.clean(cx), self.id) } } - TyTraitObject(ref bounds) => { - let lhs_ty = bounds[0].clean(cx); - match lhs_ty { - TraitBound(poly_trait, ..) => { - match poly_trait.trait_ { - ResolvedPath { path, typarams: None, did, is_generic } => { - ResolvedPath { - path: path, - typarams: Some(bounds[1..].clean(cx)), - did: did, - is_generic: is_generic, - } - } - _ => Infer // shouldn't happen + TyTraitObject(ref bounds, ref lifetime) => { + match bounds[0].clean(cx).trait_ { + ResolvedPath { path, typarams: None, did, is_generic } => { + let mut bounds: Vec<_> = bounds[1..].iter().map(|bound| { + TraitBound(bound.clean(cx), hir::TraitBoundModifier::None) + }).collect(); + if !lifetime.is_elided() { + bounds.push(RegionBound(lifetime.clean(cx))); + } + ResolvedPath { + path: path, + typarams: Some(bounds), + did: did, + is_generic: is_generic, } } _ => Infer // shouldn't happen @@ -2242,7 +2245,11 @@ impl Clean for hir::PathParameters { match *self { hir::AngleBracketedParameters(ref data) => { PathParameters::AngleBracketed { - lifetimes: data.lifetimes.clean(cx), + lifetimes: if data.lifetimes.iter().all(|lt| lt.is_elided()) { + vec![] + } else { + data.lifetimes.clean(cx) + }, types: data.types.clean(cx), bindings: data.bindings.clean(cx) } diff --git a/src/test/compile-fail/E0106.rs b/src/test/compile-fail/E0106.rs index dab03f0bccfd0..d5644ab060887 100644 --- a/src/test/compile-fail/E0106.rs +++ b/src/test/compile-fail/E0106.rs @@ -23,5 +23,17 @@ type MyStr = &str; //~^ ERROR E0106 //~| NOTE expected lifetime parameter +struct Baz<'a>(&'a str); +struct Buzz<'a, 'b>(&'a str, &'b str); + +struct Quux { + baz: Baz, + //~^ ERROR E0106 + //~| expected lifetime parameter + buzz: Buzz, + //~^ ERROR E0106 + //~| expected 2 lifetime parameters +} + fn main() { } diff --git a/src/test/compile-fail/E0107.rs b/src/test/compile-fail/E0107.rs index 5f333e17c478e..16ebd3e9ca5f2 100644 --- a/src/test/compile-fail/E0107.rs +++ b/src/test/compile-fail/E0107.rs @@ -18,9 +18,6 @@ enum Bar { } struct Baz<'a, 'b, 'c> { - foo: Foo, - //~^ ERROR E0107 - //~| expected 1 lifetime parameter buzz: Buzz<'a>, //~^ ERROR E0107 //~| expected 2 lifetime parameters diff --git a/src/test/compile-fail/associated-types-project-from-hrtb-in-struct.rs b/src/test/compile-fail/associated-types-project-from-hrtb-in-struct.rs index 44ad0bb01138f..e6251a0d318a3 100644 --- a/src/test/compile-fail/associated-types-project-from-hrtb-in-struct.rs +++ b/src/test/compile-fail/associated-types-project-from-hrtb-in-struct.rs @@ -22,10 +22,11 @@ struct SomeStruct Foo<&'x isize>> { //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context } -struct AnotherStruct Foo<&'x isize>> { - field: >::A - //~^ ERROR missing lifetime specifier -} +// FIXME(eddyb) This one doesn't even compile because of the unsupported syntax. + +// struct AnotherStruct Foo<&'x isize>> { +// field: Foo<&'y isize>>::A +// } struct YetAnotherStruct<'a, I : for<'x> Foo<&'x isize>> { field: >::A diff --git a/src/test/compile-fail/impl-trait/disallowed.rs b/src/test/compile-fail/impl-trait/disallowed.rs index 09aba5d8c9168..0467c49b0311d 100644 --- a/src/test/compile-fail/impl-trait/disallowed.rs +++ b/src/test/compile-fail/impl-trait/disallowed.rs @@ -26,9 +26,9 @@ trait LazyToString { //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types } -// Note that the following impl doesn't error, because the trait is invalid. impl LazyToString for String { fn lazy_to_string<'a>(&'a self) -> impl Fn() -> String { + //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types || self.clone() } } diff --git a/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs b/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs index 7355c70ff95e8..43371eb6340f4 100644 --- a/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs +++ b/src/test/compile-fail/lifetime-elision-return-type-requires-explicit-lifetime.rs @@ -38,4 +38,28 @@ fn i(_x: isize) -> &isize { //~ ERROR missing lifetime specifier panic!() } +// Cases which used to work but now don't. + +type StaticStr = &'static str; // hides 'static +trait WithLifetime<'a> { + type Output; // can hide 'a +} + +// This worked because the type of the first argument contains +// 'static, although StaticStr doesn't even have parameters. +fn j(_x: StaticStr) -> &isize { //~ ERROR missing lifetime specifier +//~^ HELP this function's return type contains a borrowed value +//~| HELP consider giving it an explicit bounded or 'static lifetime + panic!() +} + +// This worked because the compiler resolved the argument type +// to >::Output which has the hidden 'a. +fn k<'a, T: WithLifetime<'a>>(_x: T::Output) -> &isize { +//~^ ERROR missing lifetime specifier +//~| HELP this function's return type contains a borrowed value +//~| HELP consider giving it an explicit bounded or 'static lifetime + panic!() +} + fn main() {} diff --git a/src/test/compile-fail/region-bounds-on-objects-and-type-parameters.rs b/src/test/compile-fail/region-bounds-on-objects-and-type-parameters.rs index b8cbbdbe9ec3e..503b577b1f1b4 100644 --- a/src/test/compile-fail/region-bounds-on-objects-and-type-parameters.rs +++ b/src/test/compile-fail/region-bounds-on-objects-and-type-parameters.rs @@ -18,7 +18,7 @@ trait SomeTrait { } // Bounds on object types: -struct Foo<'a,'b,'c> { +struct Foo<'a,'b,'c> { //~ ERROR parameter `'b` is never used // All of these are ok, because we can derive exactly one bound: a: Box, b: Box>, @@ -28,7 +28,9 @@ struct Foo<'a,'b,'c> { f: Box, // OK, defaults to 'static due to RFC 599. g: Box, - z: Box+'b+'c>, //~ ERROR only a single explicit lifetime bound is permitted + z: Box+'b+'c>, + //~^ ERROR only a single explicit lifetime bound is permitted + //~| ERROR lifetime bound not satisfied } fn test< diff --git a/src/test/compile-fail/rfc1623.rs b/src/test/compile-fail/rfc1623.rs index 083cc218eecf3..93635e7fddea7 100644 --- a/src/test/compile-fail/rfc1623.rs +++ b/src/test/compile-fail/rfc1623.rs @@ -15,8 +15,10 @@ fn non_elidable<'a, 'b>(a: &'a u8, b: &'b u8) -> &'a u8 { } // the boundaries of elision -static NON_ELIDABLE_FN: &fn(&u8, &u8) -> &u8 = &(non_elidable as fn(&u8, &u8) -> &u8); +static NON_ELIDABLE_FN: &fn(&u8, &u8) -> &u8 = //~^ ERROR missing lifetime specifier [E0106] + &(non_elidable as fn(&u8, &u8) -> &u8); + //~^ ERROR missing lifetime specifier [E0106] struct SomeStruct<'x, 'y, 'z: 'x> { foo: &'x Foo<'z>, diff --git a/src/test/compile-fail/unboxed-closure-sugar-region.rs b/src/test/compile-fail/unboxed-closure-sugar-region.rs index 057b496bd43eb..18a1185d695f7 100644 --- a/src/test/compile-fail/unboxed-closure-sugar-region.rs +++ b/src/test/compile-fail/unboxed-closure-sugar-region.rs @@ -38,9 +38,9 @@ fn test<'a,'b>() { } fn test2(x: &Foo<(isize,),Output=()>, y: &Foo(isize)) { +//~^ ERROR wrong number of lifetime parameters: expected 1, found 0 // Here, the omitted lifetimes are expanded to distinct things. - same_type(x, y) //~ ERROR cannot infer - //~^ ERROR cannot infer + same_type(x, y) } fn main() { } diff --git a/src/test/compile-fail/where-equality-constraints.rs b/src/test/compile-fail/where-equality-constraints.rs index 5b2fe2901c4ce..e449a736c753a 100644 --- a/src/test/compile-fail/where-equality-constraints.rs +++ b/src/test/compile-fail/where-equality-constraints.rs @@ -10,7 +10,7 @@ fn f() where u8 = u16 {} //~^ ERROR equality constraints are not yet supported in where clauses -fn g() where for<'a> &(u8,) == u16, {} +fn g() where for<'a> &'static (u8,) == u16, {} //~^ ERROR equality constraints are not yet supported in where clauses fn main() {} diff --git a/src/test/run-pass/object-lifetime-default-from-ref-struct.rs b/src/test/run-pass/object-lifetime-default-from-ref-struct.rs index 910d933d46f08..6aaf892097136 100644 --- a/src/test/run-pass/object-lifetime-default-from-ref-struct.rs +++ b/src/test/run-pass/object-lifetime-default-from-ref-struct.rs @@ -15,6 +15,8 @@ #![allow(dead_code)] +use std::fmt::Display; + trait Test { fn foo(&self) { } } @@ -23,6 +25,11 @@ struct Ref<'a,T:'a+?Sized> { r: &'a T } +struct Ref2<'a,'b,T:'a+'b+?Sized> { + a: &'a T, + b: &'b T +} + struct SomeStruct<'a> { t: Ref<'a,Test>, u: Ref<'a,Test+'a>, @@ -44,6 +51,17 @@ fn d<'a>(t: Ref<'a,Test+'a>, mut ss: SomeStruct<'a>) { ss.u = t; } +fn e<'a>(_: Ref<'a, Display+'static>) {} +fn g<'a, 'b>(_: Ref2<'a, 'b, Display+'static>) {} + fn main() { + // Inside a function body, we can just infer all + // lifetimes, to allow Ref<'tmp, Display+'static> + // and Ref2<'tmp, 'tmp, Display+'static>. + let x = &0 as &(Display+'static); + let r: Ref = Ref { r: x }; + let r2: Ref2 = Ref2 { a: x, b: x }; + e(r); + g(r2); } diff --git a/src/test/run-pass/object-lifetime-default-from-rptr.rs b/src/test/run-pass/object-lifetime-default-from-rptr.rs index d9e0b22fbfa4b..cbff0d4dbaa3c 100644 --- a/src/test/run-pass/object-lifetime-default-from-rptr.rs +++ b/src/test/run-pass/object-lifetime-default-from-rptr.rs @@ -15,6 +15,8 @@ #![allow(dead_code)] +use std::fmt::Display; + trait Test { fn foo(&self) { } } @@ -40,6 +42,10 @@ fn d<'a>(t: &'a (Test+'a), mut ss: SomeStruct<'a>) { ss.u = t; } +fn e<'a>(_: &'a (Display+'static)) {} fn main() { + // Inside a function body, we can just infer both + // lifetimes, to allow &'tmp (Display+'static). + e(&0 as &Display); }