diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index e08da94c731..d902cb07494 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -13,18 +13,21 @@ use middle::implicator::Implication; use middle::ty::{self, FreeRegion}; use util::common::can_reach; -use util::nodemap::FnvHashMap; +use util::nodemap::{FnvHashMap, FnvHashSet}; #[derive(Clone)] pub struct FreeRegionMap { - /// `free_region_map` maps from a free region `a` to a list of + /// `map` maps from a free region `a` to a list of /// free regions `bs` such that `a <= b for all b in bs` map: FnvHashMap>, + /// regions that are required to outlive (and therefore be + /// equal to) 'static. + statics: FnvHashSet } impl FreeRegionMap { pub fn new() -> FreeRegionMap { - FreeRegionMap { map: FnvHashMap() } + FreeRegionMap { map: FnvHashMap(), statics: FnvHashSet() } } pub fn relate_free_regions_from_implications<'tcx>(&mut self, @@ -59,6 +62,8 @@ impl FreeRegionMap { } ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => { match (r_a, r_b) { + (ty::ReStatic, ty::ReFree(_)) => {}, + (ty::ReFree(fr_a), ty::ReStatic) => self.relate_to_static(fr_a), (ty::ReFree(fr_a), ty::ReFree(fr_b)) => { // Record that `'a:'b`. Or, put another way, `'b <= 'a`. self.relate_free_regions(fr_b, fr_a); @@ -76,8 +81,12 @@ impl FreeRegionMap { } } - pub fn relate_free_regions(&mut self, sub: FreeRegion, sup: FreeRegion) { - let mut sups = self.map.entry(sub).or_insert(Vec::new()); + fn relate_to_static(&mut self, sup: FreeRegion) { + self.statics.insert(sup); + } + + fn relate_free_regions(&mut self, sub: FreeRegion, sup: FreeRegion) { + let mut sups = self.map.entry(sub).or_insert(Vec::new()); if !sups.contains(&sup) { sups.push(sup); } @@ -88,7 +97,7 @@ impl FreeRegionMap { /// it is possible that `sub != sup` and `sub <= sup` and `sup <= sub` /// (that is, the user can give two different names to the same lifetime). pub fn sub_free_region(&self, sub: FreeRegion, sup: FreeRegion) -> bool { - can_reach(&self.map, sub, sup) + can_reach(&self.map, sub, sup) || self.is_static(&sup) } /// Determines whether one region is a subregion of another. This is intended to run *after @@ -116,10 +125,17 @@ impl FreeRegionMap { (ty::ReFree(sub_fr), ty::ReFree(super_fr)) => self.sub_free_region(sub_fr, super_fr), + (ty::ReStatic, ty::ReFree(ref sup_fr)) => self.is_static(sup_fr), + _ => false, } } } -} + /// Determines whether this free-region is required to be 'static + pub fn is_static(&self, super_region: &ty::FreeRegion) -> bool { + debug!("is_static(super_region={:?})", super_region); + self.statics.iter().any(|s| can_reach(&self.map, *s, *super_region)) + } +} diff --git a/src/librustc/middle/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs index 4528abfb929..4b62c7beab0 100644 --- a/src/librustc/middle/infer/region_inference/mod.rs +++ b/src/librustc/middle/infer/region_inference/mod.rs @@ -869,7 +869,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { // is the scope `s_id`. Otherwise, as we do not know // big the free region is precisely, the GLB is undefined. let fr_scope = fr.scope.to_code_extent(); - if self.tcx.region_maps.nearest_common_ancestor(fr_scope, s_id) == fr_scope { + if self.tcx.region_maps.nearest_common_ancestor(fr_scope, s_id) == fr_scope || + free_regions.is_static(fr) { Ok(s) } else { Err(TypeError::RegionsNoOverlap(b, a)) diff --git a/src/test/compile-fail/regions-static-bound.rs b/src/test/compile-fail/regions-static-bound.rs new file mode 100644 index 00000000000..297b6a866da --- /dev/null +++ b/src/test/compile-fail/regions-static-bound.rs @@ -0,0 +1,24 @@ +// Copyright 2015 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. + +fn static_id<'a,'b>(t: &'a ()) -> &'static () + where 'a: 'static { t } +fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () + where 'a: 'b, 'b: 'static { t } +fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a { + t //~ ERROR cannot infer an appropriate lifetime +} + +fn error(u: &(), v: &()) { + static_id(&u); //~ ERROR cannot infer an appropriate lifetime + static_id_indirect(&v); //~ ERROR cannot infer an appropriate lifetime +} + +fn main() {} diff --git a/src/test/run-pass/regions-static-bound.rs b/src/test/run-pass/regions-static-bound.rs new file mode 100644 index 00000000000..1c6411e3b8f --- /dev/null +++ b/src/test/run-pass/regions-static-bound.rs @@ -0,0 +1,28 @@ +// Copyright 2015 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. + +fn invariant_id<'a,'b>(t: &'b mut &'static ()) -> &'b mut &'a () + where 'a: 'static { t } +fn static_id<'a>(t: &'a ()) -> &'static () + where 'a: 'static { t } +fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static () + where 'a: 'b, 'b: 'static { t } +fn ref_id<'a>(t: &'a ()) -> &'a () where 'static: 'a { t } + +static UNIT: () = (); + +fn main() +{ + let mut val : &'static () = &UNIT; + invariant_id(&mut val); + static_id(val); + static_id_indirect(val); + ref_id(val); +}