From ba1849daecf0ae8fee54cc32f378809a9531e5ed Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Fri, 13 Jan 2017 15:09:56 +0200 Subject: [PATCH] rustc: move most of lifetime elision to resolve_lifetimes. --- src/librustc/diagnostics.rs | 63 +++ src/librustc/hir/lowering.rs | 3 +- src/librustc/middle/resolve_lifetime.rs | 502 +++++++++++++++++- src/librustc_typeck/astconv.rs | 337 ++---------- src/librustc_typeck/check/mod.rs | 26 +- src/librustc_typeck/collect.rs | 82 +-- src/librustc_typeck/diagnostics.rs | 72 +-- src/librustc_typeck/rscope.rs | 168 ------ src/test/compile-fail/E0106.rs | 12 + src/test/compile-fail/E0107.rs | 3 - ...iated-types-project-from-hrtb-in-struct.rs | 9 +- ...-return-type-requires-explicit-lifetime.rs | 24 + src/test/compile-fail/rfc1623.rs | 4 +- 13 files changed, 703 insertions(+), 602 deletions(-) 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/lowering.rs b/src/librustc/hir/lowering.rs index 4160ec5b74a92..7ca251f3ff91d 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -314,9 +314,10 @@ 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) => { + let span = Span { hi: t.span.lo, ..t.span }; let lifetime = match *region { Some(ref lt) => self.lower_lifetime(lt), - None => self.elided_lifetime(t.span) + None => self.elided_lifetime(span) }; hir::TyRptr(lifetime, self.lower_mt(mt)) } diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index c76ce9dac6fb7..4e02485a40b7d 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -22,11 +22,16 @@ 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::ptr::P; use syntax::symbol::keywords; use syntax_pos::Span; +use errors::DiagnosticBuilder; use util::nodemap::{NodeMap, FxHashSet, FxHashMap}; +use rustc_back::slice; use hir; use hir::intravisit::{self, Visitor, NestedVisitorMap}; @@ -36,6 +41,7 @@ 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), } @@ -51,9 +57,18 @@ impl Region { (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 => None, + Region::Static | + Region::LateBoundAnon(..) => None, + Region::EarlyBound(_, id) | Region::LateBound(_, id) | Region::Free(_, id) => Some(id) @@ -65,6 +80,25 @@ impl Region { 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 } } @@ -122,14 +156,46 @@ enum Scope<'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> + }, + 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(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 ScopeRef<'a> = &'a Scope<'a>; const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root; @@ -189,12 +255,19 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { hir::ItemUse(..) | hir::ItemMod(..) | hir::ItemDefaultImpl(..) | - hir::ItemForeignMod(..) | - hir::ItemStatic(..) | - hir::ItemConst(..) => { + 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) | @@ -299,6 +372,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { 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() { @@ -308,6 +382,31 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { self.resolve_lifetime_ref(lifetime_ref); } + fn visit_path_parameters(&mut self, _: Span, params: &'tcx hir::PathParameters) { + match *params { + hir::AngleBracketedParameters(ref 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); } + } + for ty in &data.types { self.visit_ty(ty); } + for b in &data.bindings { self.visit_assoc_type_binding(b); } + } + hir::ParenthesizedParameters(ref data) => { + self.visit_fn_like_elision(&data.inputs, data.output.as_ref()); + } + } + } + + 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); @@ -478,10 +577,6 @@ fn extract_labels(ctxt: &mut LifetimeContext, body: &hir::Body) { } 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)> { @@ -499,7 +594,9 @@ fn extract_labels(ctxt: &mut LifetimeContext, body: &hir::Body) { label_span: Span) { loop { match *scope { - Scope::Body { s, .. } => { scope = s; } + Scope::Body { s, .. } | + Scope::Elision { s, .. } => { scope = s; } + Scope::Root => { return; } Scope::Binder { ref lifetimes, s } => { @@ -639,6 +736,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { scope = s; } } + + Scope::Elision { s, .. } => { + scope = s; + } } }; @@ -672,6 +773,386 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } } + 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::Trait(_) | + Def::PrimTy(_) => return def == path.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; + } + } + } + } + } + + // 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) { + let delta = match ty.node { + hir::TyBareFn(_) => 1, + 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(..) => 1, + _ => 0 + } + } + _ => 0 + }; + self.binder_depth += delta; + intravisit::walk_ty(self, ty); + self.binder_depth -= delta; + } + + 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)); + } + } + } + } + } + + } + + fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[hir::Lifetime]) { + if lifetime_refs.is_empty() { + return; + } + + let span = lifetime_refs[0].span; + let mut late_depth = 0; + let mut scope = self.scope; + let error = loop { + match *scope { + // Do not assign any resolution, it will be inferred. + Scope::Body { .. } => return, + + Scope::Root => break None, + + Scope::Binder { s, .. } => { + late_depth += 1; + scope = s; + } + + 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; + } + } + }; + + 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(); + } + + 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 check_lifetime_defs(&mut self, old_scope: ScopeRef, lifetimes: &[hir::LifetimeDef]) { for i in 0..lifetimes.len() { let lifetime_i = &lifetimes[i]; @@ -729,7 +1210,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { loop { match *old_scope { - Scope::Body { s, .. } => { + Scope::Body { s, .. } | + Scope::Elision { s, .. } => { old_scope = s; } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 3338daeb77440..42a220e1b9b4b 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -61,9 +61,7 @@ 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::{RegionScope, ObjectLifetimeDefaultRscope, ShiftedRscope}; use rscope::{AnonTypeScope, MaybeWithAnonTypes, ExplicitRscope}; use util::common::{ErrorReported, FN_OUTPUT_NAME}; use util::nodemap::{NodeMap, FxHashSet}; @@ -74,7 +72,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 +108,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>) + -> &'tcx ty::Region; + /// What type should we use when a type is omitted? fn ty_infer(&self, span: Span) -> Ty<'tcx>; @@ -161,94 +162,16 @@ 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)); -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 ast_region_to_region(&self, lifetime: &hir::Lifetime) -> &'tcx ty::Region { - self.opt_ast_region_to_region(&ExplicitRscope, lifetime.span, Some(lifetime), None) - } - - fn try_opt_ast_region_to_region(&self, - rscope: &RegionScope, - default_span: Span, - opt_lifetime: Option<&hir::Lifetime>, + pub fn ast_region_to_region(&self, + lifetime: &hir::Lifetime, def: Option<&ty::RegionParameterDef>) - -> Result<&'tcx ty::Region, Option>> + -> &'tcx ty::Region { let tcx = self.tcx(); - let name = opt_lifetime.map(|l| l.name); - let resolved = opt_lifetime.and_then(|l| tcx.named_region_map.defs.get(&l.id)); - let r = tcx.mk_region(match resolved { + let r = match tcx.named_region_map.defs.get(&lifetime.id) { Some(&rl::Region::Static) => { - ty::ReStatic + tcx.mk_region(ty::ReStatic) } Some(&rl::Region::LateBound(debruijn, id)) => { @@ -263,16 +186,21 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { .get(&id) .cloned() .unwrap_or(ty::Issue32330::WontChange); - ty::ReLateBound(debruijn, ty::BrNamed(tcx.hir.local_def_id(id), - name.unwrap(), - issue_32330)) + 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::Region::LateBoundAnon(debruijn, index)) => { + tcx.mk_region(ty::ReLateBound(debruijn, ty::BrAnon(index))) } - Some(&rl::Region::EarlyBound(index, _)) => { - ty::ReEarlyBound(ty::EarlyBoundRegion { + Some(&rl::Region::EarlyBound(index, id)) => { + let name = tcx.hir.name(id); + tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { index: index, - name: name.unwrap() - }) + name: name + })) } Some(&rl::Region::Free(scope, id)) => { @@ -283,47 +211,23 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { .get(&id) .cloned() .unwrap_or(ty::Issue32330::WontChange); - ty::ReFree(ty::FreeRegion { - scope: scope.to_code_extent(&tcx.region_maps), - bound_region: ty::BrNamed(tcx.hir.local_def_id(id), - name.unwrap(), - issue_32330) - }) + 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), name, issue_32330) + })) // (*) -- not late-bound, won't change } - None => rscope.anon_region(default_span, def)? - }); + None => self.re_infer(lifetime.span, def) + }; - debug!("opt_ast_region_to_region(opt_lifetime={:?}) yields {:?}", - opt_lifetime, + debug!("ast_region_to_region(lifetime={:?}) yields {:?}", + lifetime, r); - Ok(r) - } - - pub fn opt_ast_region_to_region(&self, - rscope: &RegionScope, - default_span: Span, - opt_lifetime: Option<&hir::Lifetime>, - def: Option<&ty::RegionParameterDef>) -> &'tcx ty::Region - { - let tcx = self.tcx(); - self.try_opt_ast_region_to_region(rscope, default_span, opt_lifetime, def) - .unwrap_or_else(|params| { - let ampersand_span = Span { hi: default_span.lo, ..default_span}; - - let mut err = struct_span_err!(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(tcx, &mut err, params); - } - err.emit(); - tcx.mk_region(ty::ReStatic) - }) + r } /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`, @@ -404,20 +308,10 @@ 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 mut reported_lifetime_count_mismatch = false; - let mut report_lifetime_count_mismatch = || { - if !reported_lifetime_count_mismatch { - reported_lifetime_count_mismatch = true; - let all_infer = lifetimes.iter().all(|lt| lt.is_elided()); - let supplied = if all_infer { 0 } else { supplied_num_region_params }; - report_lifetime_number_error(tcx, span, - supplied, - expected_num_region_params); - } - }; - if expected_num_region_params != supplied_num_region_params { - report_lifetime_count_mismatch(); + report_lifetime_number_error(tcx, span, + supplied_num_region_params, + expected_num_region_params); } // If a self-type was declared, one should be provided. @@ -445,11 +339,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; - let l = lifetimes.get(i); - self.try_opt_ast_region_to_region(rscope, span, l, Some(def)).unwrap_or_else(|_| { - report_lifetime_count_mismatch(); + 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; @@ -533,72 +427,6 @@ 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>], @@ -606,19 +434,14 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { -> (Ty<'tcx>, ConvertedBinding<'tcx>) { let anon_scope = rscope.anon_type_scope(); - let binding_rscope = MaybeWithAnonTypes::new(BindingRscope::new(), anon_scope); + let rscope = MaybeWithAnonTypes::new(ExplicitRscope, 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_arg_to_ty(&rscope, None, region_substs, 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(&rscope, output_ty), output_ty.span) } None => { (self.tcx().mk_nil(), data.span) @@ -1469,7 +1292,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { }) } hir::TyRptr(ref region, ref mt) => { - let r = self.opt_ast_region_to_region(rscope, ast_ty.span, Some(region), None); + let r = self.ast_region_to_region(region, None); debug!("TyRef r={:?}", r); let rscope1 = &ObjectLifetimeDefaultRscope::new( @@ -1489,9 +1312,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { 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); @@ -1626,37 +1447,19 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } } - 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) + pub fn ty_of_fn(&self, + unsafety: hir::Unsafety, + abi: abi::Abi, + decl: &hir::FnDecl, + anon_scope: Option) + -> &'tcx ty::BareFnTy<'tcx> { + self.ty_of_method_or_bare_fn(unsafety, abi, decl, 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> @@ -1665,40 +1468,14 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { // 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); + let rb = MaybeWithAnonTypes::new(ExplicitRscope, arg_anon_scope); 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) - - } - }; - let output_ty = match decl.output { hir::Return(ref output) => - self.convert_ty_with_lifetime_elision(implied_output_region, - &output, - ret_anon_scope), + self.ast_ty_to_ty(&MaybeWithAnonTypes::new(ExplicitRscope, ret_anon_scope), output), hir::DefaultReturn(..) => self.tcx().mk_nil(), }; @@ -1725,10 +1502,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 @@ -1739,7 +1512,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { None } }); - self.ty_of_arg(&rb, a, expected_arg_ty) + self.ty_of_arg(&ExplicitRscope, a, expected_arg_ty) }); let expected_ret_ty = expected_sig.as_ref().map(|e| e.output()); @@ -1755,7 +1528,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(&ExplicitRscope, &output), hir::DefaultReturn(..) => bug!(), }; @@ -1820,7 +1593,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { if let Some(&r) = explicit_region_bounds.get(0) { // Explicitly specified region bound. Use that. - return Some(self.ast_region_to_region(r)); + return Some(self.ast_region_to_region(r, None)); } if let Some(principal) = existential_predicates.principal() { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index e7544c10be3e7..a8b5d718f81d0 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -97,7 +97,7 @@ 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 rscope::RegionScope; use session::{Session, CompileResult}; use CrateCtxt; use TypeAndSubsts; @@ -1410,6 +1410,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>) + -> &'tcx ty::Region { + let v = match def { + Some(def) => infer::EarlyBoundRegion(span, def.name), + None => infer::MiscVariable(span) + }; + self.next_region_var(v) + } + fn ty_infer(&self, span: Span) -> Ty<'tcx> { self.next_ty_var(TypeVariableOrigin::TypeInference(span)) } @@ -1465,15 +1474,6 @@ impl<'a, 'gcx, 'tcx> RegionScope for FnCtxt<'a, 'gcx, 'tcx> { // be some expression). *self.next_region_var(infer::MiscVariable(span)) } - - fn anon_region(&self, span: Span, def: Option<&ty::RegionParameterDef>) - -> Result>> { - let v = match def { - Some(def) => infer::EarlyBoundRegion(span, def.name), - None => infer::MiscVariable(span) - }; - Ok(*self.next_region_var(v)) - } } /// Controls whether the arguments are tupled. This is used for the call @@ -4408,7 +4408,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { None => &[] }; - AstConv::opt_ast_region_to_region(self, self, span, lifetimes.get(i), Some(def)) + if let Some(lifetime) = lifetimes.get(i) { + AstConv::ast_region_to_region(self, lifetime, Some(def)) + } else { + self.re_infer(span, Some(def)) + } }, |def, substs| { let mut i = def.index as usize; diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index f954d2a5d6168..70bd43751ebdf 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -437,6 +437,11 @@ impl<'a, 'tcx> AstConv<'tcx, 'tcx> for ItemCtxt<'a, 'tcx> { None } + fn re_infer(&self, span: Span, _def: Option<&ty::RegionParameterDef>) + -> &'tcx ty::Region { + span_bug!(span, "unelided lifetime in signature"); + } + fn ty_infer(&self, span: Span) -> Ty<'tcx> { struct_span_err!( self.tcx().sess, @@ -639,8 +644,6 @@ 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); @@ -652,14 +655,8 @@ fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, 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, anon_scope); let substs = mk_item_substs(&ccx.icx(&(rcvr_ty_predicates, &sig.generics)), ccx.tcx.hir.span(id), def_id); @@ -876,14 +873,9 @@ fn convert_trait_item(ccx: &CrateCtxt, trait_item: &hir::TraitItem) { 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) - }; + hir::TraitItemKind::Method(ref sig, _) => { convert_method(ccx, TraitContainer(trait_def_id), - trait_item.id, sig, tcx.mk_self_type(), - body, &trait_predicates); + trait_item.id, sig, &trait_predicates); } } } @@ -896,7 +888,6 @@ 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, _) => { @@ -923,10 +914,8 @@ fn convert_impl_item(ccx: &CrateCtxt, impl_item: &hir::ImplItem) { 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, ImplContainer(impl_def_id), impl_item.id, sig, &impl_predicates); } } } @@ -1472,7 +1461,7 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, index: own_start + i as u32, def_id: tcx.hir.local_def_id(l.lifetime.id), bounds: l.bounds.iter().map(|l| { - AstConv::ast_region_to_region(&ccx.icx(&()), l) + AstConv::ast_region_to_region(&ccx.icx(&()), l, None) }).collect(), pure_wrt_drop: l.pure_wrt_drop, } @@ -1545,11 +1534,11 @@ 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(&ExplicitRscope, &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, + Some(AnonTypeScope::new(def_id))); let substs = mk_item_substs(&ccx.icx(generics), item.span, def_id); ccx.tcx.mk_fn_def(def_id, substs, tofd) } @@ -1765,7 +1754,7 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, name: param.lifetime.name })); for bound in ¶m.bounds { - let bound_region = AstConv::ast_region_to_region(&ccx.icx(&()), 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()); } @@ -1816,7 +1805,9 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, } &hir::TyParamBound::RegionTyParamBound(ref lifetime) => { - let region = AstConv::ast_region_to_region(&ccx.icx(&()), 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 +1816,9 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, } &hir::WherePredicate::RegionPredicate(ref region_pred) => { - let r1 = AstConv::ast_region_to_region(&ccx.icx(&()), ®ion_pred.lifetime); + let r1 = AstConv::ast_region_to_region(&ccx.icx(&()), ®ion_pred.lifetime, None); for bound in ®ion_pred.bounds { - let r2 = AstConv::ast_region_to_region(&ccx.icx(&()), 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)) } @@ -1935,7 +1926,7 @@ fn compute_object_lifetime_default<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, hir::TraitTyParamBound(..) => None, hir::RegionTyParamBound(ref lifetime) => - Some(AstConv::ast_region_to_region(&ccx.icx(&()), lifetime)), + Some(AstConv::ast_region_to_region(&ccx.icx(&()), lifetime, None)), } }) .collect() @@ -1997,7 +1988,7 @@ pub fn compute_bounds<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>, }).collect(); let region_bounds = region_bounds.into_iter().map(|r| { - astconv.ast_region_to_region(r) + astconv.ast_region_to_region(r, None) }).collect(); trait_bounds.sort_by(|a,b| a.def_id().cmp(&b.def_id())); @@ -2039,7 +2030,7 @@ fn predicates_from_bound<'tcx>(astconv: &AstConv<'tcx, 'tcx>, .collect() } hir::RegionTyParamBound(ref lifetime) => { - let region = astconv.ast_region_to_region(lifetime); + let region = astconv.ast_region_to_region(lifetime, None); let pred = ty::Binder(ty::OutlivesPredicate(param_ty, region)); vec![ty::Predicate::TypeOutlives(pred)] } @@ -2057,18 +2048,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, None); // feature gate SIMD types in FFI, since I (huonw) am not sure the // ABIs are handled at all correctly. @@ -2084,21 +2064,17 @@ 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>, diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 6d943f3ca2ef6..7f8c508bf2224 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##" diff --git a/src/librustc_typeck/rscope.rs b/src/librustc_typeck/rscope.rs index 2ad1a7c3d685f..3ac917c396cf1 100644 --- a/src/librustc_typeck/rscope.rs +++ b/src/librustc_typeck/rscope.rs @@ -8,28 +8,14 @@ // 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 @@ -41,9 +27,6 @@ pub type ElidedLifetime = Result>>; /// can return `Err(())` to indicate that this is not a scope in which /// regions can legally be omitted. pub trait RegionScope { - fn anon_region(&self, span: Span, def: Option<&ty::RegionParameterDef>) - -> Result>>; - /// 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. @@ -115,11 +98,6 @@ impl RegionScope for MaybeWithAnonTypes { self.base_scope.object_lifetime_default(span) } - fn anon_region(&self, span: Span, def: Option<&ty::RegionParameterDef>) - -> Result>> { - self.base_scope.anon_region(span, def) - } - fn base_object_lifetime_default(&self, span: Span) -> ty::Region { self.base_scope.base_object_lifetime_default(span) } @@ -135,105 +113,6 @@ impl RegionScope for MaybeWithAnonTypes { pub struct ExplicitRscope; impl RegionScope for ExplicitRscope { - fn anon_region(&self, _span: Span, _: Option<&ty::RegionParameterDef>) - -> Result>> { - 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_region(&self, _span: Span, _: Option<&ty::RegionParameterDef>) - -> Result>> { - Err(self.0.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_region(&self, _span: Span, _: Option<&ty::RegionParameterDef>) - -> Result>> - { - Ok(self.default) - } -} - -/// 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_region(&self, span: Span, _: Option<&ty::RegionParameterDef>) - -> Result>> { - 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(ty::ReStatic) - } - fn object_lifetime_default(&self, span: Span) -> Option { Some(self.base_object_lifetime_default(span)) } @@ -243,41 +122,6 @@ impl<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> RegionScope for StaticRscope<'a, 'gcx, 'tcx> } } -/// 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), - } - } -} - -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_region(&self, _: Span, _: Option<&ty::RegionParameterDef>) - -> Result>> - { - let idx = self.anon_bindings.get(); - self.anon_bindings.set(idx + 1); - Ok(ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrAnon(idx))) - } -} - /// A scope which overrides the default object lifetime but has no other effect. pub struct ObjectLifetimeDefaultRscope<'r> { base_scope: &'r (RegionScope+'r), @@ -315,12 +159,6 @@ impl<'r> RegionScope for ObjectLifetimeDefaultRscope<'r> { self.base_scope.base_object_lifetime_default(span) } - fn anon_region(&self, span: Span, def: Option<&ty::RegionParameterDef>) - -> Result>> - { - self.base_scope.anon_region(span, def) - } - fn anon_type_scope(&self) -> Option { self.base_scope.anon_type_scope() } @@ -348,12 +186,6 @@ impl<'r> RegionScope for ShiftedRscope<'r> { ty::fold::shift_region(self.base_scope.base_object_lifetime_default(span), 1) } - fn anon_region(&self, span: Span, def: Option<&ty::RegionParameterDef>) - -> Result>> - { - self.base_scope.anon_region(span, def).map(|r| ty::fold::shift_region(r, 1)) - } - fn anon_type_scope(&self) -> Option { self.base_scope.anon_type_scope() } 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/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/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>,