From 2d99ffd11b28357ed989a9641ed94ea384659d51 Mon Sep 17 00:00:00 2001 From: gaurikholkar Date: Tue, 20 Jun 2017 06:42:11 -0700 Subject: [PATCH] track anonymous regions in return types, fix tidy errors --- src/librustc/infer/error_reporting/mod.rs | 2 +- .../error_reporting/named_anon_conflict.rs | 53 +++++++++++++------ ...-one-existing-name-if-else-using-impl-3.rs | 2 +- ...n-one-existing-name-return-type-is-anon.rs | 24 +++++++++ ...e-existing-name-return-type-is-anon.stderr | 27 ++++++++++ 5 files changed, 91 insertions(+), 17 deletions(-) create mode 100644 src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.rs create mode 100644 src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.stderr diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index b3f7f2d37644..9e2d922b932d 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -272,7 +272,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { for error in errors { debug!("report_region_errors: error = {:?}", error); - + if !self.try_report_named_anon_conflict(&error){ match error.clone() { diff --git a/src/librustc/infer/error_reporting/named_anon_conflict.rs b/src/librustc/infer/error_reporting/named_anon_conflict.rs index 8af9381107b8..edb7887b504b 100644 --- a/src/librustc/infer/error_reporting/named_anon_conflict.rs +++ b/src/librustc/infer/error_reporting/named_anon_conflict.rs @@ -15,6 +15,7 @@ use ty::{self, Region}; use infer::region_inference::RegionResolutionError::*; use infer::region_inference::RegionResolutionError; use hir::map as hir_map; +use hir::def_id::DefId; impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // This method walks the Type of the function body arguments using @@ -24,13 +25,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // Currently only the case where the function declaration consists of // one named region and one anonymous region is handled. // Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32` - // Here, we would return the hir::Arg for y, and we return the type &'a + // Here, we would return the hir::Arg for y, we return the type &'a // i32, which is the type of y but with the anonymous region replaced - // with 'a. + // with 'a and also the corresponding bound region. fn find_arg_with_anonymous_region(&self, anon_region: Region<'tcx>, named_region: Region<'tcx>) - -> Option<(&hir::Arg, ty::Ty<'tcx>)> { + -> Option<(&hir::Arg, ty::Ty<'tcx>, ty::BoundRegion)> { match *anon_region { ty::ReFree(ref free_region) => { @@ -55,7 +56,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { r }); if found_anon_region { - return Some((arg, new_arg_ty)); + return Some((arg, new_arg_ty, free_region.bound_region)); } else { None } @@ -85,15 +86,36 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // only introduced anonymous regions in parameters) as well as a // version new_ty of its type where the anonymous region is replaced // with the named one. - let (named, (arg, new_ty)) = - if self.is_named_region(sub) && self.is_suitable_anonymous_region(sup) { - (sub, self.find_arg_with_anonymous_region(sup, sub).unwrap()) - } else if self.is_named_region(sup) && self.is_suitable_anonymous_region(sub) { - (sup, self.find_arg_with_anonymous_region(sub, sup).unwrap()) + let (named, (arg, new_ty, br), scope_def_id) = + if self.is_named_region(sub) && self.is_suitable_anonymous_region(sup).is_some() { + (sub, + self.find_arg_with_anonymous_region(sup, sub).unwrap(), + self.is_suitable_anonymous_region(sup).unwrap()) + } else if self.is_named_region(sup) && + self.is_suitable_anonymous_region(sub).is_some() { + (sup, + self.find_arg_with_anonymous_region(sub, sup).unwrap(), + self.is_suitable_anonymous_region(sub).unwrap()) } else { return false; // inapplicable }; + // Here, we check for the case where the anonymous region + // is in the return type. + // FIXME(#42703) - Need to handle certain cases here. + let ret_ty = self.tcx.type_of(scope_def_id); + match ret_ty.sty { + ty::TyFnDef(_, _, sig) => { + let late_bound_regions = self.tcx + .collect_referenced_late_bound_regions(&sig.output()); + if late_bound_regions.iter().any(|r| *r == br) { + return false; + } else { + } + } + _ => {} + } + if let Some(simple_name) = arg.pat.simple_name() { struct_span_err!(self.tcx.sess, span, @@ -122,7 +144,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } // This method returns whether the given Region is Anonymous - pub fn is_suitable_anonymous_region(&self, region: Region<'tcx>) -> bool { + // and returns the DefId corresponding to the region. + pub fn is_suitable_anonymous_region(&self, region: Region<'tcx>) -> Option { match *region { ty::ReFree(ref free_region) => { @@ -147,20 +170,20 @@ associated_item(anonymous_region_binding_scope).container.id()).is_some() { // since the signature must match the trait. // // FIXME(#42706) -- in some cases, we could do better here. - return false;//None; + return None; } else{ } } - _ => return false, // inapplicable + _ => return None, // inapplicable // we target only top-level functions } - return true; + return Some(anonymous_region_binding_scope); } - _ => false, + _ => None, } } - _ => false, + _ => None, } } } diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs index 60f794279a52..362290ff3fa7 100644 --- a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-impl-3.rs @@ -14,7 +14,7 @@ struct Foo { impl Foo { fn foo<'a>(&'a self, x: &i32) -> &i32 { - + if true { &self.field } else { x } } diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.rs new file mode 100644 index 000000000000..96b733be9b4e --- /dev/null +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.rs @@ -0,0 +1,24 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct Foo { + field: i32 +} + +impl Foo { + fn foo<'a>(&self, x: &'a i32) -> &i32 { + + x + + } + +} + +fn main() { } diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.stderr new file mode 100644 index 000000000000..e32de589d287 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-return-type-is-anon.stderr @@ -0,0 +1,27 @@ +error[E0312]: lifetime of reference outlives lifetime of borrowed content... + --> $DIR/ex1-return-one-existing-name-return-type-is-anon.rs:18:5 + | +18 | x + | ^ + | +note: ...the reference is valid for the anonymous lifetime #1 defined on the method body at 16:3... + --> $DIR/ex1-return-one-existing-name-return-type-is-anon.rs:16:3 + | +16 | / fn foo<'a>(&self, x: &'a i32) -> &i32 { +17 | | +18 | | x +19 | | +20 | | } + | |___^ +note: ...but the borrowed content is only valid for the lifetime 'a as defined on the method body at 16:3 + --> $DIR/ex1-return-one-existing-name-return-type-is-anon.rs:16:3 + | +16 | / fn foo<'a>(&self, x: &'a i32) -> &i32 { +17 | | +18 | | x +19 | | +20 | | } + | |___^ + +error: aborting due to previous error(s) +