Skip to content

Commit

Permalink
After inferring regions, scan for any bounds that are due to a lifetime
Browse files Browse the repository at this point in the history
bound that is likely to change. In that case, it will change to 'static,
so then scan down the graph to see whether there are any hard
constraints that would prevent 'static from being a valid value
here. Report a warning.
  • Loading branch information
nikomatsakis committed Jul 3, 2015
1 parent 9099577 commit 1e7a6b8
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 0 deletions.
36 changes: 36 additions & 0 deletions src/librustc/diagnostics.rs
Expand Up @@ -1160,6 +1160,42 @@ static mut FOO: Option<Box<usize>> = None;
// error: mutable statics are not allowed to have destructors
static mut BAR: Option<Vec<i32>> = None;
```
"##,

E0398: r##"
In Rust 1.3, the default object lifetime bounds are expected to
change, as described in RFC #1156 [1]. You are getting a warning
because the compiler thinks it is possible that this change will cause
a compilation error in your code. It is possible, though unlikely,
that this is a false alarm.
The heart of the change is that where `&'a Box<SomeTrait>` used to
default to `&'a Box<SomeTrait+'a>`, it now defaults to `&'a
Box<SomeTrait+'static>` (here, `SomeTrait` is the name of some trait
type). Note that the only types which are affected are references to
boxes, like `&Box<SomeTrait>` or `&[Box<SomeTrait>]`. More common
types like `&SomeTrait` or `Box<SomeTrait>` are unaffected.
To silence this warning, edit your code to use an explicit bound.
Most of the time, this means that you will want to change the
signature of a function that you are calling. For example, if
the error is reported on a call like `foo(x)`, and `foo` is
defined as follows:
```
fn foo(arg: &Box<SomeTrait>) { ... }
```
you might change it to:
```
fn foo<'a>(arg: &Box<SomeTrait+'a>) { ... }
```
This explicitly states that you expect the trait object `SomeTrait` to
contain references (with a maximum lifetime of `'a`).
[1]: https://github.com/rust-lang/rfcs/pull/1156
"##

}
Expand Down
47 changes: 47 additions & 0 deletions src/librustc/middle/infer/region_inference/mod.rs
Expand Up @@ -1358,9 +1358,56 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
}
}

// Check for future hostile edges tied to a bad default
self.report_future_hostility(&graph);

(0..self.num_vars() as usize).map(|idx| var_data[idx].value).collect()
}

fn report_future_hostility(&self, graph: &RegionGraph) {
let constraints = self.constraints.borrow();
for edge in graph.all_edges() {
match constraints[&edge.data] {
SubregionOrigin::DefaultExistentialBound(_) => {
// this will become 'static in the future
}
_ => { continue; }
}

// this constraint will become a 'static constraint in the
// future, so walk outward and see if we have any hard
// bounds that could not be inferred to 'static
for nid in graph.depth_traverse(edge.target()) {
for (_, succ) in graph.outgoing_edges(nid) {
match succ.data {
ConstrainVarSubReg(_, r) => {
match r {
ty::ReStatic | ty::ReInfer(_) => {
/* OK */
}
ty::ReFree(_) | ty::ReScope(_) | ty::ReEmpty => {
span_warn!(
self.tcx.sess,
constraints[&edge.data].span(),
E0398,
"this code may fail to compile in Rust 1.3 due to \
the proposed change in object lifetime bound defaults");
return; // only issue the warning once per fn
}
ty::ReEarlyBound(..) | ty::ReLateBound(..) => {
self.tcx.sess.span_bug(
constraints[&succ.data].span(),
"relation to bound region");
}
}
}
_ => { }
}
}
}
}
}

fn construct_graph(&self) -> RegionGraph {
let num_vars = self.num_vars();

Expand Down
21 changes: 21 additions & 0 deletions src/test/auxiliary/lifetime_bound_will_change_warning_lib.rs
@@ -0,0 +1,21 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <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.

#![crate_type = "rlib"]

// Helper for testing that we get suitable warnings when lifetime
// bound change will cause breakage.

pub fn just_ref(x: &Fn()) {
}

pub fn ref_obj(x: &Box<Fn()>) {
// this will change to &Box<Fn()+'static>...
}
64 changes: 64 additions & 0 deletions src/test/compile-fail/lifetime-bound-will-change-warning.rs
@@ -0,0 +1,64 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <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.

// aux-build:lifetime_bound_will_change_warning_lib.rs

// Test that we get suitable warnings when lifetime bound change will
// cause breakage.

#![allow(dead_code)]
#![allow(unused_variables)]
#![feature(rustc_attrs)]

extern crate lifetime_bound_will_change_warning_lib as lib;

fn just_ref(x: &Fn()) {
}

fn ref_obj(x: &Box<Fn()>) {
// this will change to &Box<Fn()+'static>...

// Note: no warning is issued here, because the type of `x` will change to 'static
if false { ref_obj(x); }
}

fn test1<'a>(x: &'a Box<Fn()+'a>) {
// just_ref will stay the same.
just_ref(&**x)
}

fn test1cc<'a>(x: &'a Box<Fn()+'a>) {
// same as test1, but cross-crate
lib::just_ref(&**x)
}

fn test2<'a>(x: &'a Box<Fn()+'a>) {
// but ref_obj will not, so warn.
ref_obj(x) //~ WARNING this code may fail to compile in Rust 1.3
}

fn test2cc<'a>(x: &'a Box<Fn()+'a>) {
// same as test2, but cross crate
lib::ref_obj(x) //~ WARNING this code may fail to compile in Rust 1.3
}

fn test3<'a>(x: &'a Box<Fn()+'static>) {
// here, we have a 'static bound, so even when ref_obj changes, no error results
ref_obj(x)
}

fn test3cc<'a>(x: &'a Box<Fn()+'static>) {
// same as test3, but cross crate
lib::ref_obj(x)
}

#[rustc_error]
fn main() { //~ ERROR compilation successful
}

0 comments on commit 1e7a6b8

Please sign in to comment.