Erroneous code example:
```compile_fail,E0312
-fn make_child<'human, 'elve>(x: &mut &'human isize, y: &mut &'elve isize) {
- *x = *y;
- // error: lifetime of reference outlives lifetime of borrowed content
+fn make_child<'tree, 'human>(
+ x: &'human i32,
+ y: &'tree i32
+) -> &'human i32 {
+ if x > y
+ { x }
+ else
+ { y }
+ // error: lifetime of reference outlives lifetime of borrowed content
}
```
-The compiler cannot determine if the `human` lifetime will live long enough
-to keep up on the elve one. To solve this error, you have to give an
-explicit lifetime hierarchy:
+The function declares that it returns a reference with the `'human`
+lifetime, but it may return data with the `'tree` lifetime. As neither
+lifetime is declared longer than the other, this results in an
+error. Sometimes, this error is because the function *body* is
+incorrect -- that is, maybe you did not *mean* to return data from
+`y`. In that case, you should fix the function body.
+
+Often, however, the body is correct. In that case, the function
+signature needs to be altered to match the body, so that the caller
+understands that data from either `x` or `y` may be returned. The
+simplest way to do this is to give both function parameters the *same*
+named lifetime:
```
-fn make_child<'human, 'elve: 'human>(x: &mut &'human isize,
- y: &mut &'elve isize) {
- *x = *y; // ok!
+fn make_child<'human>(
+ x: &'human i32,
+ y: &'human i32
+) -> &'human i32 {
+ if x > y
+ { x }
+ else
+ { y } // ok!
}
```
-Or use the same lifetime for every variable:
+However, in some cases, you may prefer to explicitly declare that one lifetime
+outlives another using a `where` clause:
```
-fn make_child<'elve>(x: &mut &'elve isize, y: &mut &'elve isize) {
- *x = *y; // ok!
+fn make_child<'tree, 'human>(
+ x: &'human i32,
+ y: &'tree i32
+) -> &'tree i32
+where
+ 'tree: 'human
+{
+ if x > y
+ { x }
+ else
+ { y } // ok!
}
```
+
+Here, the where clause `'tree: 'human` can be read as "the lifetime
+'tree outlives the lifetime 'human" -- meaning, references with the
+`'tree` lifetime live *at least as long as* references with the
+`'human` lifetime. Therefore, it is safe to return data with lifetime
+`'tree` when data with the lifetime `'human` is needed.
"##,
E0317: r##"
+++ /dev/null
-// Copyright 2012-2013 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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Error Reporting for Anonymous Region Lifetime Errors
-//! where both the regions are anonymous.
-use hir;
-use infer::InferCtxt;
-use ty::{self, Region};
-use infer::region_inference::RegionResolutionError::*;
-use infer::region_inference::RegionResolutionError;
-use hir::map as hir_map;
-use middle::resolve_lifetime as rl;
-use hir::intravisit::{self, Visitor, NestedVisitorMap};
-
-impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
- // This method prints the error message for lifetime errors when both the concerned regions
- // are anonymous.
- // Consider a case where we have
- // fn foo(x: &mut Vec<&u8>, y: &u8)
- // { x.push(y); }.
- // The example gives
- // fn foo(x: &mut Vec<&u8>, y: &u8) {
- // --- --- these references are declared with different lifetimes...
- // x.push(y);
- // ^ ...but data from `y` flows into `x` here
- // It has been extended for the case of structs too.
- // Consider the example
- // struct Ref<'a> { x: &'a u32 }
- // fn foo(mut x: Vec<Ref>, y: Ref) {
- // --- --- these structs are declared with different lifetimes...
- // x.push(y);
- // ^ ...but data from `y` flows into `x` here
- // }
- // It will later be extended to trait objects.
- pub fn try_report_anon_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool {
- let (span, sub, sup) = match *error {
- ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),
- _ => return false, // inapplicable
- };
-
- // Determine whether the sub and sup consist of both anonymous (elided) regions.
- let anon_reg_sup = or_false!(self.is_suitable_anonymous_region(sup));
-
- let anon_reg_sub = or_false!(self.is_suitable_anonymous_region(sub));
- let scope_def_id_sup = anon_reg_sup.def_id;
- let bregion_sup = anon_reg_sup.boundregion;
- let scope_def_id_sub = anon_reg_sub.def_id;
- let bregion_sub = anon_reg_sub.boundregion;
-
- let ty_sup = or_false!(self.find_anon_type(sup, &bregion_sup));
-
- let ty_sub = or_false!(self.find_anon_type(sub, &bregion_sub));
-
- let (main_label, label1, label2) = if let (Some(sup_arg), Some(sub_arg)) =
- (self.find_arg_with_anonymous_region(sup, sup),
- self.find_arg_with_anonymous_region(sub, sub)) {
-
- let (anon_arg_sup, is_first_sup, anon_arg_sub, is_first_sub) =
- (sup_arg.arg, sup_arg.is_first, sub_arg.arg, sub_arg.is_first);
- if self.is_self_anon(is_first_sup, scope_def_id_sup) ||
- self.is_self_anon(is_first_sub, scope_def_id_sub) {
- return false;
- }
-
- if self.is_return_type_anon(scope_def_id_sup, bregion_sup) ||
- self.is_return_type_anon(scope_def_id_sub, bregion_sub) {
- return false;
- }
-
- if anon_arg_sup == anon_arg_sub {
- (format!("this type was declared with multiple lifetimes..."),
- format!(" with one lifetime"),
- format!(" into the other"))
- } else {
- let span_label_var1 = if let Some(simple_name) = anon_arg_sup.pat.simple_name() {
- format!(" from `{}`", simple_name)
- } else {
- format!("")
- };
-
- let span_label_var2 = if let Some(simple_name) = anon_arg_sub.pat.simple_name() {
- format!(" into `{}`", simple_name)
- } else {
- format!("")
- };
-
- let span_label =
- format!("these two types are declared with different lifetimes...",);
-
- (span_label, span_label_var1, span_label_var2)
- }
- } else {
- return false;
- };
-
- struct_span_err!(self.tcx.sess, span, E0623, "lifetime mismatch")
- .span_label(ty_sup.span, main_label)
- .span_label(ty_sub.span, format!(""))
- .span_label(span, format!("...but data{} flows{} here", label1, label2))
- .emit();
- return true;
- }
-
- /// This function calls the `visit_ty` method for the parameters
- /// corresponding to the anonymous regions. The `nested_visitor.found_type`
- /// contains the anonymous type.
- ///
- /// # Arguments
- /// region - the anonymous region corresponding to the anon_anon conflict
- /// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
- ///
- /// # Example
- /// ```
- /// fn foo(x: &mut Vec<&u8>, y: &u8)
- /// { x.push(y); }
- /// ```
- /// The function returns the nested type corresponding to the anonymous region
- /// for e.g. `&u8` and Vec<`&u8`.
- pub fn find_anon_type(&self, region: Region<'tcx>, br: &ty::BoundRegion) -> Option<&hir::Ty> {
- if let Some(anon_reg) = self.is_suitable_anonymous_region(region) {
- let def_id = anon_reg.def_id;
- if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
- let ret_ty = self.tcx.type_of(def_id);
- if let ty::TyFnDef(_, _) = ret_ty.sty {
- let inputs: &[_] =
- match self.tcx.hir.get(node_id) {
- hir_map::NodeItem(&hir::Item {
- node: hir::ItemFn(ref fndecl, ..), ..
- }) => &fndecl.inputs,
- hir_map::NodeTraitItem(&hir::TraitItem {
- node: hir::TraitItemKind::Method(ref fndecl, ..),
- ..
- }) => &fndecl.decl.inputs,
- hir_map::NodeImplItem(&hir::ImplItem {
- node: hir::ImplItemKind::Method(ref fndecl, ..),
- ..
- }) => &fndecl.decl.inputs,
-
- _ => &[],
- };
-
- return inputs
- .iter()
- .filter_map(|arg| {
- self.find_component_for_bound_region(&**arg, br)
- })
- .next();
- }
- }
- }
- None
- }
-
- // This method creates a FindNestedTypeVisitor which returns the type corresponding
- // to the anonymous region.
- fn find_component_for_bound_region(&self,
- arg: &'gcx hir::Ty,
- br: &ty::BoundRegion)
- -> Option<(&'gcx hir::Ty)> {
- let mut nested_visitor = FindNestedTypeVisitor {
- infcx: &self,
- hir_map: &self.tcx.hir,
- bound_region: *br,
- found_type: None,
- };
- nested_visitor.visit_ty(arg);
- nested_visitor.found_type
- }
-}
-
-// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
-// anonymous region. The example above would lead to a conflict between
-// the two anonymous lifetimes for &u8 in x and y respectively. This visitor
-// would be invoked twice, once for each lifetime, and would
-// walk the types like &mut Vec<&u8> and &u8 looking for the HIR
-// where that lifetime appears. This allows us to highlight the
-// specific part of the type in the error message.
-struct FindNestedTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
- infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
- hir_map: &'a hir::map::Map<'gcx>,
- // The bound_region corresponding to the Refree(freeregion)
- // associated with the anonymous region we are looking for.
- bound_region: ty::BoundRegion,
- // The type where the anonymous lifetime appears
- // for e.g. Vec<`&u8`> and <`&u8`>
- found_type: Option<&'gcx hir::Ty>,
-}
-
-impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
- NestedVisitorMap::OnlyBodies(&self.hir_map)
- }
-
- fn visit_ty(&mut self, arg: &'gcx hir::Ty) {
- // Find the index of the anonymous region that was part of the
- // error. We will then search the function parameters for a bound
- // region at the right depth with the same index.
- let br_index = match self.bound_region {
- ty::BrAnon(index) => index,
- _ => return,
- };
-
- match arg.node {
- hir::TyRptr(ref lifetime, _) => {
- let hir_id = self.infcx.tcx.hir.node_to_hir_id(lifetime.id);
- match self.infcx.tcx.named_region(hir_id) {
- // the lifetime of the TyRptr
- Some(rl::Region::LateBoundAnon(debruijn_index, anon_index)) => {
- if debruijn_index.depth == 1 && anon_index == br_index {
- self.found_type = Some(arg);
- return; // we can stop visiting now
- }
- }
- Some(rl::Region::Static) |
- Some(rl::Region::EarlyBound(_, _)) |
- Some(rl::Region::LateBound(_, _)) |
- Some(rl::Region::Free(_, _)) |
- None => {
- debug!("no arg found");
- }
- }
- }
- // Checks if it is of type `hir::TyPath` which corresponds to a struct.
- hir::TyPath(_) => {
- let subvisitor = &mut TyPathVisitor {
- infcx: self.infcx,
- found_it: false,
- bound_region: self.bound_region,
- hir_map: self.hir_map,
- };
- intravisit::walk_ty(subvisitor, arg); // call walk_ty; as visit_ty is empty,
- // this will visit only outermost type
- if subvisitor.found_it {
- self.found_type = Some(arg);
- }
- }
- _ => {}
- }
- // walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
- // go on to visit `&Foo`
- intravisit::walk_ty(self, arg);
- }
-}
-
-// The visitor captures the corresponding `hir::Ty` of the anonymous region
-// in the case of structs ie. `hir::TyPath`.
-// This visitor would be invoked for each lifetime corresponding to a struct,
-// and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR
-// where that lifetime appears. This allows us to highlight the
-// specific part of the type in the error message.
-struct TyPathVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
- infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
- hir_map: &'a hir::map::Map<'gcx>,
- found_it: bool,
- bound_region: ty::BoundRegion,
-}
-
-impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> {
- fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
- NestedVisitorMap::OnlyBodies(&self.hir_map)
- }
-
- fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
- let br_index = match self.bound_region {
- ty::BrAnon(index) => index,
- _ => return,
- };
-
- let hir_id = self.infcx.tcx.hir.node_to_hir_id(lifetime.id);
- match self.infcx.tcx.named_region(hir_id) {
- // the lifetime of the TyPath!
- Some(rl::Region::LateBoundAnon(debruijn_index, anon_index)) => {
- if debruijn_index.depth == 1 && anon_index == br_index {
- self.found_it = true;
- }
- }
- Some(rl::Region::Static) |
- Some(rl::Region::EarlyBound(_, _)) |
- Some(rl::Region::LateBound(_, _)) |
- Some(rl::Region::Free(_, _)) |
- None => {
- debug!("no arg found");
- }
- }
- }
-
- fn visit_ty(&mut self, arg: &'gcx hir::Ty) {
- // ignore nested types
- //
- // If you have a type like `Foo<'a, &Ty>` we
- // are only interested in the immediate lifetimes ('a).
- //
- // Making `visit_ty` empty will ignore the `&Ty` embedded
- // inside, it will get reached by the outer visitor.
- debug!("`Ty` corresponding to a struct is {:?}", arg);
- }
-}
--- /dev/null
+// Copyright 2012-2013 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Error Reporting for Anonymous Region Lifetime Errors
+//! where both the regions are anonymous.
+use hir;
+use infer::InferCtxt;
+use ty::{self, Region};
+use infer::region_inference::RegionResolutionError::*;
+use infer::region_inference::RegionResolutionError;
+use hir::map as hir_map;
+use middle::resolve_lifetime as rl;
+use hir::intravisit::{self, Visitor, NestedVisitorMap};
+
+impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
+ // This method prints the error message for lifetime errors when both the concerned regions
+ // are anonymous.
+ // Consider a case where we have
+ // fn foo(x: &mut Vec<&u8>, y: &u8)
+ // { x.push(y); }.
+ // The example gives
+ // fn foo(x: &mut Vec<&u8>, y: &u8) {
+ // --- --- these references are declared with different lifetimes...
+ // x.push(y);
+ // ^ ...but data from `y` flows into `x` here
+ // It has been extended for the case of structs too.
+ // Consider the example
+ // struct Ref<'a> { x: &'a u32 }
+ // fn foo(mut x: Vec<Ref>, y: Ref) {
+ // --- --- these structs are declared with different lifetimes...
+ // x.push(y);
+ // ^ ...but data from `y` flows into `x` here
+ // }
+ // It will later be extended to trait objects.
+ pub fn try_report_anon_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool {
+ let (span, sub, sup) = match *error {
+ ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),
+ _ => return false, // inapplicable
+ };
+
+ // Determine whether the sub and sup consist of both anonymous (elided) regions.
+ let anon_reg_sup = or_false!(self.is_suitable_region(sup));
+
+ let anon_reg_sub = or_false!(self.is_suitable_region(sub));
+ let scope_def_id_sup = anon_reg_sup.def_id;
+ let bregion_sup = anon_reg_sup.boundregion;
+ let scope_def_id_sub = anon_reg_sub.def_id;
+ let bregion_sub = anon_reg_sub.boundregion;
+
+ let ty_sup = or_false!(self.find_anon_type(sup, &bregion_sup));
+
+ let ty_sub = or_false!(self.find_anon_type(sub, &bregion_sub));
+ debug!("try_report_anon_anon_conflict: found_arg1={:?} sup={:?} br1={:?}",
+ ty_sub,
+ sup,
+ bregion_sup);
+ debug!("try_report_anon_anon_conflict: found_arg2={:?} sub={:?} br2={:?}",
+ ty_sup,
+ sub,
+ bregion_sub);
+
+ let (main_label, label1, label2) = if let (Some(sup_arg), Some(sub_arg)) =
+ (self.find_arg_with_region(sup, sup), self.find_arg_with_region(sub, sub)) {
+
+ let (anon_arg_sup, is_first_sup, anon_arg_sub, is_first_sub) =
+ (sup_arg.arg, sup_arg.is_first, sub_arg.arg, sub_arg.is_first);
+ if self.is_self_anon(is_first_sup, scope_def_id_sup) ||
+ self.is_self_anon(is_first_sub, scope_def_id_sub) {
+ return false;
+ }
+
+ if self.is_return_type_anon(scope_def_id_sup, bregion_sup) ||
+ self.is_return_type_anon(scope_def_id_sub, bregion_sub) {
+ return false;
+ }
+
+ if anon_arg_sup == anon_arg_sub {
+ (format!("this type was declared with multiple lifetimes..."),
+ format!(" with one lifetime"),
+ format!(" into the other"))
+ } else {
+ let span_label_var1 = if let Some(simple_name) = anon_arg_sup.pat.simple_name() {
+ format!(" from `{}`", simple_name)
+ } else {
+ format!("")
+ };
+
+ let span_label_var2 = if let Some(simple_name) = anon_arg_sub.pat.simple_name() {
+ format!(" into `{}`", simple_name)
+ } else {
+ format!("")
+ };
+
+ let span_label =
+ format!("these two types are declared with different lifetimes...",);
+
+ (span_label, span_label_var1, span_label_var2)
+ }
+ } else {
+ debug!("no arg with anon region found");
+ debug!("try_report_anon_anon_conflict: is_suitable(sub) = {:?}",
+ self.is_suitable_region(sub));
+ debug!("try_report_anon_anon_conflict: is_suitable(sup) = {:?}",
+ self.is_suitable_region(sup));
+ return false;
+ };
+
+ struct_span_err!(self.tcx.sess, span, E0623, "lifetime mismatch")
+ .span_label(ty_sup.span, main_label)
+ .span_label(ty_sub.span, format!(""))
+ .span_label(span, format!("...but data{} flows{} here", label1, label2))
+ .emit();
+ return true;
+ }
+
+ /// This function calls the `visit_ty` method for the parameters
+ /// corresponding to the anonymous regions. The `nested_visitor.found_type`
+ /// contains the anonymous type.
+ ///
+ /// # Arguments
+ /// region - the anonymous region corresponding to the anon_anon conflict
+ /// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
+ ///
+ /// # Example
+ /// ```
+ /// fn foo(x: &mut Vec<&u8>, y: &u8)
+ /// { x.push(y); }
+ /// ```
+ /// The function returns the nested type corresponding to the anonymous region
+ /// for e.g. `&u8` and Vec<`&u8`.
+ pub fn find_anon_type(&self, region: Region<'tcx>, br: &ty::BoundRegion) -> Option<&hir::Ty> {
+ if let Some(anon_reg) = self.is_suitable_region(region) {
+ let def_id = anon_reg.def_id;
+ if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
+ let inputs: &[_] = match self.tcx.hir.get(node_id) {
+ hir_map::NodeItem(&hir::Item { node: hir::ItemFn(ref fndecl, ..), .. }) => {
+ &fndecl.inputs
+ }
+ hir_map::NodeTraitItem(&hir::TraitItem {
+ node: hir::TraitItemKind::Method(ref fndecl, ..), ..
+ }) => &fndecl.decl.inputs,
+ hir_map::NodeImplItem(&hir::ImplItem {
+ node: hir::ImplItemKind::Method(ref fndecl, ..), ..
+ }) => &fndecl.decl.inputs,
+
+ _ => &[],
+ };
+
+ return inputs
+ .iter()
+ .filter_map(|arg| self.find_component_for_bound_region(&**arg, br))
+ .next();
+ }
+ }
+ None
+ }
+
+ // This method creates a FindNestedTypeVisitor which returns the type corresponding
+ // to the anonymous region.
+ fn find_component_for_bound_region(&self,
+ arg: &'gcx hir::Ty,
+ br: &ty::BoundRegion)
+ -> Option<(&'gcx hir::Ty)> {
+ let mut nested_visitor = FindNestedTypeVisitor {
+ infcx: &self,
+ hir_map: &self.tcx.hir,
+ bound_region: *br,
+ found_type: None,
+ };
+ nested_visitor.visit_ty(arg);
+ nested_visitor.found_type
+ }
+}
+
+// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
+// anonymous region. The example above would lead to a conflict between
+// the two anonymous lifetimes for &u8 in x and y respectively. This visitor
+// would be invoked twice, once for each lifetime, and would
+// walk the types like &mut Vec<&u8> and &u8 looking for the HIR
+// where that lifetime appears. This allows us to highlight the
+// specific part of the type in the error message.
+struct FindNestedTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
+ infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+ hir_map: &'a hir::map::Map<'gcx>,
+ // The bound_region corresponding to the Refree(freeregion)
+ // associated with the anonymous region we are looking for.
+ bound_region: ty::BoundRegion,
+ // The type where the anonymous lifetime appears
+ // for e.g. Vec<`&u8`> and <`&u8`>
+ found_type: Option<&'gcx hir::Ty>,
+}
+
+impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
+ NestedVisitorMap::OnlyBodies(&self.hir_map)
+ }
+
+ fn visit_ty(&mut self, arg: &'gcx hir::Ty) {
+ match arg.node {
+ hir::TyRptr(ref lifetime, _) => {
+ // the lifetime of the TyRptr
+ let hir_id = self.infcx.tcx.hir.node_to_hir_id(lifetime.id);
+ match (self.infcx.tcx.named_region(hir_id), self.bound_region) {
+ // Find the index of the anonymous region that was part of the
+ // error. We will then search the function parameters for a bound
+ // region at the right depth with the same index
+ (Some(rl::Region::LateBoundAnon(debruijn_index, anon_index)),
+ ty::BrAnon(br_index)) => {
+ debug!("LateBoundAnon depth = {:?} anon_index = {:?} br_index={:?}",
+ debruijn_index.depth,
+ anon_index,
+ br_index);
+ if debruijn_index.depth == 1 && anon_index == br_index {
+ self.found_type = Some(arg);
+ return; // we can stop visiting now
+ }
+ }
+
+ // Find the index of the named region that was part of the
+ // error. We will then search the function parameters for a bound
+ // region at the right depth with the same index
+ (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => {
+ debug!("EarlyBound self.infcx.tcx.hir.local_def_id(id)={:?} \
+ def_id={:?}",
+ self.infcx.tcx.hir.local_def_id(id),
+ def_id);
+ if self.infcx.tcx.hir.local_def_id(id) == def_id {
+ self.found_type = Some(arg);
+ return; // we can stop visiting now
+ }
+ }
+
+ // Find the index of the named region that was part of the
+ // error. We will then search the function parameters for a bound
+ // region at the right depth with the same index
+ (Some(rl::Region::LateBound(debruijn_index, id)), ty::BrNamed(def_id, _)) => {
+ debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}",
+ debruijn_index.depth);
+ debug!("self.infcx.tcx.hir.local_def_id(id)={:?}",
+ self.infcx.tcx.hir.local_def_id(id));
+ debug!("def_id={:?}", def_id);
+ if debruijn_index.depth == 1 &&
+ self.infcx.tcx.hir.local_def_id(id) == def_id {
+ self.found_type = Some(arg);
+ return; // we can stop visiting now
+ }
+ }
+
+ (Some(rl::Region::Static), _) |
+ (Some(rl::Region::Free(_, _)), _) |
+ (Some(rl::Region::EarlyBound(_, _)), _) |
+ (Some(rl::Region::LateBound(_, _)), _) |
+ (Some(rl::Region::LateBoundAnon(_, _)), _) |
+ (None, _) => {
+ debug!("no arg found");
+ }
+ }
+ }
+ // Checks if it is of type `hir::TyPath` which corresponds to a struct.
+ hir::TyPath(_) => {
+ let subvisitor = &mut TyPathVisitor {
+ infcx: self.infcx,
+ found_it: false,
+ bound_region: self.bound_region,
+ hir_map: self.hir_map,
+ };
+ intravisit::walk_ty(subvisitor, arg); // call walk_ty; as visit_ty is empty,
+ // this will visit only outermost type
+ if subvisitor.found_it {
+ self.found_type = Some(arg);
+ }
+ }
+ _ => {}
+ }
+ // walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
+ // go on to visit `&Foo`
+ intravisit::walk_ty(self, arg);
+ }
+}
+
+// The visitor captures the corresponding `hir::Ty` of the anonymous region
+// in the case of structs ie. `hir::TyPath`.
+// This visitor would be invoked for each lifetime corresponding to a struct,
+// and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR
+// where that lifetime appears. This allows us to highlight the
+// specific part of the type in the error message.
+struct TyPathVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
+ infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+ hir_map: &'a hir::map::Map<'gcx>,
+ found_it: bool,
+ bound_region: ty::BoundRegion,
+}
+
+impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> {
+ fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
+ NestedVisitorMap::OnlyBodies(&self.hir_map)
+ }
+
+ fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
+ let br_index = match self.bound_region {
+ ty::BrAnon(index) => index,
+ _ => return,
+ };
+
+ let hir_id = self.infcx.tcx.hir.node_to_hir_id(lifetime.id);
+ match self.infcx.tcx.named_region(hir_id) {
+ // the lifetime of the TyPath!
+ Some(rl::Region::LateBoundAnon(debruijn_index, anon_index)) => {
+ if debruijn_index.depth == 1 && anon_index == br_index {
+ self.found_it = true;
+ }
+ }
+ Some(rl::Region::Static) |
+ Some(rl::Region::EarlyBound(_, _)) |
+ Some(rl::Region::LateBound(_, _)) |
+ Some(rl::Region::Free(_, _)) |
+ None => {
+ debug!("no arg found");
+ }
+ }
+ }
+
+ fn visit_ty(&mut self, arg: &'gcx hir::Ty) {
+ // ignore nested types
+ //
+ // If you have a type like `Foo<'a, &Ty>` we
+ // are only interested in the immediate lifetimes ('a).
+ //
+ // Making `visit_ty` empty will ignore the `&Ty` embedded
+ // inside, it will get reached by the outer visitor.
+ debug!("`Ty` corresponding to a struct is {:?}", arg);
+ }
+}
mod named_anon_conflict;
#[macro_use]
mod util;
-mod anon_anon_conflict;
+mod different_lifetimes;
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
pub fn note_and_explain_region(self,
use infer::InferCtxt;
use infer::region_inference::RegionResolutionError::*;
use infer::region_inference::RegionResolutionError;
+use ty;
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// This method generates the error message for the case when
_ => return false, // inapplicable
};
+ debug!("try_report_named_anon_conflict(sub={:?}, sup={:?})",
+ sub,
+ sup);
+
// Determine whether the sub and sup consist of one named region ('a)
// and one anonymous (elided) region. If so, find the parameter arg
// where the anonymous region appears (there must always be one; we
// version new_ty of its type where the anonymous region is replaced
// with the named one.//scope_def_id
let (named, anon_arg_info, region_info) =
- if sub.is_named_region() && self.is_suitable_anonymous_region(sup).is_some() {
+ if self.is_named_region(sub) && self.is_suitable_region(sup).is_some() &&
+ self.find_arg_with_region(sup, sub).is_some() {
(sub,
- self.find_arg_with_anonymous_region(sup, sub).unwrap(),
- self.is_suitable_anonymous_region(sup).unwrap())
- } else if sup.is_named_region() && self.is_suitable_anonymous_region(sub).is_some() {
+ self.find_arg_with_region(sup, sub).unwrap(),
+ self.is_suitable_region(sup).unwrap())
+ } else if self.is_named_region(sup) && self.is_suitable_region(sub).is_some() &&
+ self.find_arg_with_region(sub, sup).is_some() {
(sup,
- self.find_arg_with_anonymous_region(sub, sup).unwrap(),
- self.is_suitable_anonymous_region(sub).unwrap())
+ self.find_arg_with_region(sub, sup).unwrap(),
+ self.is_suitable_region(sub).unwrap())
} else {
return false; // inapplicable
};
+ debug!("try_report_named_anon_conflict: named = {:?}", named);
+ debug!("try_report_named_anon_conflict: anon_arg_info = {:?}",
+ anon_arg_info);
+ debug!("try_report_named_anon_conflict: region_info = {:?}",
+ region_info);
+
let (arg, new_ty, br, is_first, scope_def_id, is_impl_item) = (anon_arg_info.arg,
anon_arg_info.arg_ty,
anon_arg_info.bound_region,
anon_arg_info.is_first,
region_info.def_id,
region_info.is_impl_item);
+ match br {
+ ty::BrAnon(_) => {}
+ _ => {
+ /* not an anonymous region */
+ debug!("try_report_named_anon_conflict: not an anonymous region");
+ return false;
+ }
+ }
+
if is_impl_item {
+ debug!("try_report_named_anon_conflict: impl item, bail out");
return false;
}
- if self.is_return_type_anon(scope_def_id, br) || self.is_self_anon(is_first, scope_def_id) {
+ if self.is_return_type_anon(scope_def_id, br) {
+ debug!("try_report_named_anon_conflict: is_return_type_anon({:?}, {:?}) = true",
+ scope_def_id,
+ br);
+ return false;
+ } else if self.is_self_anon(is_first, scope_def_id) {
+ debug!("try_report_named_anon_conflict: is_self_anon({:?}, {:?}) = true",
+ is_first,
+ scope_def_id);
return false;
} else {
-
let (error_var, span_label_var) = if let Some(simple_name) = arg.pat.simple_name() {
(format!("the type of `{}`", simple_name), format!("the type of `{}`", simple_name))
} else {
format!("consider changing {} to `{}`", span_label_var, new_ty))
.span_label(span, format!("lifetime `{}` required", named))
.emit();
-
-
+ return true;
}
- return true;
}
}
macro_rules! or_false {
($v:expr) => {
- match $v {
- Some(v) => v,
- None => return false,
- }
+ match $v {
+ Some(v) => v,
+ None => {
+ debug!("or_false failed: {}", stringify!($v));
+ return false;
+ }
+ }
}
}
// The struct contains the information about the anonymous region
// we are searching for.
+#[derive(Debug)]
pub struct AnonymousArgInfo<'tcx> {
// the argument corresponding to the anonymous region
pub arg: &'tcx hir::Arg,
// This struct contains information regarding the
// Refree((FreeRegion) corresponding to lifetime conflict
+#[derive(Debug)]
pub struct FreeRegionInfo {
// def id corresponding to FreeRegion
pub def_id: DefId,
// i32, which is the type of y but with the anonymous region replaced
// with 'a, the corresponding bound region and is_first which is true if
// the hir::Arg is the first argument in the function declaration.
- pub fn find_arg_with_anonymous_region(&self,
- anon_region: Region<'tcx>,
- replace_region: Region<'tcx>)
- -> Option<AnonymousArgInfo> {
-
- if let ty::ReFree(ref free_region) = *anon_region {
- let id = free_region.scope;
- let hir = &self.tcx.hir;
- if let Some(node_id) = hir.as_local_node_id(id) {
- if let Some(body_id) = hir.maybe_body_owned_by(node_id) {
- let body = hir.body(body_id);
- if let Some(tables) = self.in_progress_tables {
- body.arguments
- .iter()
- .enumerate()
- .filter_map(|(index, arg)| {
- let ty = tables.borrow().node_id_to_type(arg.hir_id);
- let mut found_anon_region = false;
- let new_arg_ty = self.tcx
- .fold_regions(&ty, &mut false, |r, _| if *r == *anon_region {
- found_anon_region = true;
- replace_region
- } else {
- r
- });
- if found_anon_region {
- let is_first = index == 0;
- Some(AnonymousArgInfo {
- arg: arg,
- arg_ty: new_arg_ty,
- bound_region: free_region.bound_region,
- is_first: is_first,
- })
+ pub fn find_arg_with_region(&self,
+ anon_region: Region<'tcx>,
+ replace_region: Region<'tcx>)
+ -> Option<AnonymousArgInfo> {
+
+ let (id, bound_region) = match *anon_region {
+ ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region),
+ ty::ReEarlyBound(ref ebr) => {
+ (self.tcx.parent_def_id(ebr.def_id).unwrap(),
+ ty::BoundRegion::BrNamed(ebr.def_id, ebr.name))
+ }
+ _ => return None, // not a free region
+ };
+
+ let hir = &self.tcx.hir;
+ if let Some(node_id) = hir.as_local_node_id(id) {
+ if let Some(body_id) = hir.maybe_body_owned_by(node_id) {
+ let body = hir.body(body_id);
+ if let Some(tables) = self.in_progress_tables {
+ body.arguments
+ .iter()
+ .enumerate()
+ .filter_map(|(index, arg)| {
+ let ty = match tables.borrow().node_id_to_type_opt(arg.hir_id) {
+ Some(v) => v,
+ None => return None, // sometimes the tables are not yet populated
+ };
+ let mut found_anon_region = false;
+ let new_arg_ty = self.tcx
+ .fold_regions(&ty, &mut false, |r, _| if *r == *anon_region {
+ found_anon_region = true;
+ replace_region
} else {
- None
- }
- })
- .next()
- } else {
- None
- }
+ r
+ });
+ if found_anon_region {
+ let is_first = index == 0;
+ Some(AnonymousArgInfo {
+ arg: arg,
+ arg_ty: new_arg_ty,
+ bound_region: bound_region,
+ is_first: is_first,
+ })
+ } else {
+ None
+ }
+ })
+ .next()
} else {
None
}
}
}
- // This method returns whether the given Region is Anonymous
- // and returns the DefId and the BoundRegion corresponding to the given region.
- pub fn is_suitable_anonymous_region(&self, region: Region<'tcx>) -> Option<FreeRegionInfo> {
- if let ty::ReFree(ref free_region) = *region {
- if let ty::BrAnon(..) = free_region.bound_region {
- let anonymous_region_binding_scope = free_region.scope;
- let node_id = self.tcx
- .hir
- .as_local_node_id(anonymous_region_binding_scope)
- .unwrap();
- let mut is_impl_item = false;
- match self.tcx.hir.find(node_id) {
-
- Some(hir_map::NodeItem(..)) |
- Some(hir_map::NodeTraitItem(..)) => {
- // Success -- proceed to return Some below
- }
- Some(hir_map::NodeImplItem(..)) => {
- is_impl_item =
- self.is_bound_region_in_impl_item(anonymous_region_binding_scope);
- }
- _ => return None,
- }
- return Some(FreeRegionInfo {
- def_id: anonymous_region_binding_scope,
- boundregion: free_region.bound_region,
- is_impl_item: is_impl_item,
- });
+ // This method returns the DefId and the BoundRegion corresponding to the given region.
+ pub fn is_suitable_region(&self, region: Region<'tcx>) -> Option<FreeRegionInfo> {
+
+ let (suitable_region_binding_scope, bound_region) = match *region {
+ ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region),
+ ty::ReEarlyBound(ref ebr) => {
+ (self.tcx.parent_def_id(ebr.def_id).unwrap(),
+ ty::BoundRegion::BrNamed(ebr.def_id, ebr.name))
}
- }
- None
+ _ => return None, // not a free region
+ };
+
+ let node_id = self.tcx
+ .hir
+ .as_local_node_id(suitable_region_binding_scope)
+ .unwrap();
+ let is_impl_item = match self.tcx.hir.find(node_id) {
+
+ Some(hir_map::NodeItem(..)) |
+ Some(hir_map::NodeTraitItem(..)) => false,
+ Some(hir_map::NodeImplItem(..)) => {
+ self.is_bound_region_in_impl_item(suitable_region_binding_scope)
+ }
+ _ => return None,
+ };
+
+ return Some(FreeRegionInfo {
+ def_id: suitable_region_binding_scope,
+ boundregion: bound_region,
+ is_impl_item: is_impl_item,
+ });
+
}
// Here, we check for the case where the anonymous region
}
// Here we check if the bound region is in Impl Item.
- pub fn is_bound_region_in_impl_item(&self, anonymous_region_binding_scope: DefId) -> bool {
+ pub fn is_bound_region_in_impl_item(&self, suitable_region_binding_scope: DefId) -> bool {
let container_id = self.tcx
- .associated_item(anonymous_region_binding_scope)
+ .associated_item(suitable_region_binding_scope)
.container
.id();
if self.tcx.impl_trait_ref(container_id).is_some() {
}
false
}
+
+ // This method returns whether the given Region is Named
+ pub fn is_named_region(&self, region: Region<'tcx>) -> bool {
+ match *region {
+ ty::ReFree(ref free_region) => {
+ match free_region.bound_region {
+ ty::BrNamed(..) => true,
+ _ => false,
+ }
+ }
+ _ => false,
+ }
+ }
}
flags
}
-
- // This method returns whether the given Region is Named
- pub fn is_named_region(&self) -> bool {
- match *self {
- ty::ReFree(ref free_region) => {
- match free_region.bound_region {
- ty::BrNamed(..) => true,
- _ => false,
- }
- }
- _ => false,
- }
- }
}
/// Type utilities
// Note that &'static T <: &'a T.
let a: <T as Trait<'a>>::Type = loop { };
let b: <T as Trait<'b>>::Type = loop { };
- let _: <T as Trait<'b>>::Type = a; //~ ERROR mismatched types
+ let _: <T as Trait<'b>>::Type = a; //~ ERROR E0623
}
fn method3<'a,'b,T>(x: &'a T, y: &'b T)
// Note that &'static T <: &'a T.
let a: <T as Trait<'a>>::Type = loop { };
let b: <T as Trait<'b>>::Type = loop { };
- let _: <T as Trait<'a>>::Type = b; //~ ERROR mismatched types
+ let _: <T as Trait<'a>>::Type = b; //~ ERROR E0623
}
fn method4<'a,'b,T>(x: &'a T, y: &'b T)
fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
// Illegal now because there is no `'b:'a` declaration.
- *x = *y; //~ ERROR E0312
+ *x = *y; //~ ERROR E0623
}
fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
// Illegal now because there is no `'b:'a` declaration.
- *x = *y; //~ ERROR E0312
- *z = *y; //~ ERROR E0312
+ *x = *y; //~ ERROR E0623
+ *z = *y; //~ ERROR E0623
}
fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
struct Paramd<'a> { x: &'a usize }
fn call2<'a, 'b>(a: &'a usize, b: &'b usize) {
- let z: Option<&'b &'a usize> = None;
- //~^ ERROR reference has a longer lifetime than the data it references
+ let z: Option<&'b &'a usize> = None;//~ ERROR E0623
}
fn call3<'a, 'b>(a: &'a usize, b: &'b usize) {
let y: Paramd<'a> = Paramd { x: a };
- let z: Option<&'b Paramd<'a>> = None;
- //~^ ERROR reference has a longer lifetime than the data it references
+ let z: Option<&'b Paramd<'a>> = None;//~ ERROR E0623
}
fn call4<'a, 'b>(a: &'a usize, b: &'b usize) {
- let z: Option<&'a &'b usize> = None;
- //~^ ERROR reference has a longer lifetime than the data it references
+ let z: Option<&'a &'b usize> = None;//~ ERROR E0623
}
-
fn main() {}
// 'short <= 'long, this would be true if the Contravariant type were
// covariant with respect to its parameter 'a.
- let _: Contravariant<'long> = c; //~ ERROR mismatched types
+ let _: Contravariant<'long> = c; //~ ERROR E0623
}
fn main() {}
// 'short <= 'long, this would be true if the Covariant type were
// contravariant with respect to its parameter 'a.
- let _: Covariant<'short> = c; //~ ERROR mismatched types
+ let _: Covariant<'short> = c; //~ ERROR E0623
}
fn main() {}
fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
// Illegal now because there is no `'b:'a` declaration.
- *x = *y; //~ ERROR E0312
+ *x = *y; //~ ERROR E0623
}
fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
// 'short <= 'long, this would be true if the Contravariant type were
// covariant with respect to its parameter 'a.
- let _: S<'long, 'long> = c; //~ ERROR mismatched types
+ let _: S<'long, 'long> = c; //~ ERROR E0623
}
fn main() {}
// 'short <= 'long, this would be true if the Contravariant type were
// covariant with respect to its parameter 'a.
- let _: Contravariant<'long> = c; //~ ERROR mismatched types
+ let _: Contravariant<'long> = c; //~ ERROR E0623
}
fn main() {}
// 'short <= 'long, this would be true if the Covariant type were
// contravariant with respect to its parameter 'a.
- let _: Covariant<'short> = c; //~ ERROR mismatched types
+ let _: Covariant<'short> = c; //~ ERROR E0623
}
fn main() {}
// 'short <= 'long, this would be true if the Invariant type were
// contravariant with respect to its parameter 'a.
- let _: Invariant<'short> = c; //~ ERROR mismatched types
+ let _: Invariant<'short> = c; //~ ERROR E0623
}
fn main() { }
s: &'short isize,
l: &'long isize,
_where:Option<&'short &'long ()>) {
- let _: Foo<'long> = c; //~ ERROR mismatched types
+ let _: Foo<'long> = c; //~ ERROR E0623
}
fn main() {
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+trait Foo<'a> {}
+impl<'a, T> Foo<'a> for T {}
+
+fn baz<'a, 'b, T>(x: &mut Vec<&'a T>, y: &T)
+ where i32: Foo<'a>,
+ u32: Foo<'b>
+{
+ x.push(y);
+}
+fn main() {
+let x = baz;
+}
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/ex3-both-anon-regions-earlybound-regions.rs:17:12
+ |
+13 | fn baz<'a, 'b, T>(x: &mut Vec<&'a T>, y: &T)
+ | ----- -- these two types are declared with different lifetimes...
+...
+17 | x.push(y);
+ | ^ ...but data from `y` flows into `x` here
+
+error: aborting due to previous error
+
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn foo<'a,'b>(x: &mut Vec<&'a u8>, y: &'b u8) {
+ x.push(y);
+}
+
+fn main() { }
\ No newline at end of file
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/ex3-both-anon-regions-latebound-regions.rs:12:12
+ |
+11 | fn foo<'a,'b>(x: &mut Vec<&'a u8>, y: &'b u8) {
+ | ------ ------ these two types are declared with different lifetimes...
+12 | x.push(y);
+ | ^ ...but data from `y` flows into `x` here
+
+error: aborting due to previous error
+