Skip to content

Commit e33bef6

Browse files
committed
lifetimes lint: walk type bounds as well as types (fixes rust-lang#253, again)
1 parent 9d49cc5 commit e33bef6

File tree

2 files changed

+47
-13
lines changed

2 files changed

+47
-13
lines changed

src/lifetimes.rs

+35-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use syntax::ast::*;
22
use rustc::lint::*;
33
use syntax::codemap::Span;
4-
use syntax::visit::{Visitor, walk_ty};
4+
use syntax::visit::{Visitor, walk_ty, walk_ty_param_bound};
55
use std::collections::HashSet;
66

77
use utils::{in_external_macro, span_lint};
@@ -68,14 +68,7 @@ fn could_use_elision(func: &FnDecl, slf: Option<&ExplicitSelf>,
6868
// level of the current item.
6969

7070
// check named LTs
71-
let mut allowed_lts = HashSet::new();
72-
for lt in named_lts {
73-
if lt.bounds.is_empty() {
74-
allowed_lts.insert(Named(lt.lifetime.name));
75-
}
76-
}
77-
allowed_lts.insert(Unnamed);
78-
allowed_lts.insert(Static);
71+
let allowed_lts = allowed_lts_from(named_lts);
7972

8073
// these will collect all the lifetimes for references in arg/return types
8174
let mut input_visitor = RefVisitor(Vec::new());
@@ -142,6 +135,18 @@ fn could_use_elision(func: &FnDecl, slf: Option<&ExplicitSelf>,
142135
false
143136
}
144137

138+
fn allowed_lts_from(named_lts: &[LifetimeDef]) -> HashSet<RefLt> {
139+
let mut allowed_lts = HashSet::new();
140+
for lt in named_lts {
141+
if lt.bounds.is_empty() {
142+
allowed_lts.insert(Named(lt.lifetime.name));
143+
}
144+
}
145+
allowed_lts.insert(Unnamed);
146+
allowed_lts.insert(Static);
147+
allowed_lts
148+
}
149+
145150
/// Number of unique lifetimes in the given vector.
146151
fn unique_lifetimes(lts: &[RefLt]) -> usize {
147152
lts.iter().collect::<HashSet<_>>().len()
@@ -186,17 +191,34 @@ impl<'v> Visitor<'v> for RefVisitor {
186191
/// Are any lifetimes mentioned in the `where` clause? If yes, we don't try to
187192
/// reason about elision.
188193
fn has_where_lifetimes(where_clause: &WhereClause) -> bool {
189-
let mut where_visitor = RefVisitor(Vec::new());
190194
for predicate in &where_clause.predicates {
191195
match *predicate {
192196
WherePredicate::RegionPredicate(..) => return true,
193197
WherePredicate::BoundPredicate(ref pred) => {
194-
walk_ty(&mut where_visitor, &pred.bounded_ty);
198+
// a predicate like F: Trait or F: for<'a> Trait<'a>
199+
let mut visitor = RefVisitor(Vec::new());
200+
// walk the type F, it may not contain LT refs
201+
walk_ty(&mut visitor, &pred.bounded_ty);
202+
if !visitor.0.is_empty() { return true; }
203+
// if the bounds define new lifetimes, they are fine to occur
204+
let allowed_lts = allowed_lts_from(&pred.bound_lifetimes);
205+
// now walk the bounds
206+
for bound in pred.bounds.iter() {
207+
walk_ty_param_bound(&mut visitor, bound);
208+
}
209+
// and check that all lifetimes are allowed
210+
for lt in visitor.into_vec() {
211+
if !allowed_lts.contains(&lt) {
212+
return true;
213+
}
214+
}
195215
}
196216
WherePredicate::EqPredicate(ref pred) => {
197-
walk_ty(&mut where_visitor, &pred.ty);
217+
let mut visitor = RefVisitor(Vec::new());
218+
walk_ty(&mut visitor, &pred.ty);
219+
if !visitor.0.is_empty() { return true; }
198220
}
199221
}
200222
}
201-
!where_visitor.into_vec().is_empty()
223+
false
202224
}

tests/compile-fail/lifetimes.rs

+12
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) { } // no error, bounde
4646

4747
fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8) where 'b: 'a { } // no error, bounded lifetime
4848

49+
struct Lt<'a, I: 'static> {
50+
x: &'a I
51+
}
52+
53+
fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
54+
where F: Fn(Lt<'a, I>) -> Lt<'a, I> // no error, fn bound references 'a
55+
{ unreachable!() }
56+
57+
fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> //~ERROR explicit lifetimes given
58+
where for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>
59+
{ unreachable!() }
60+
4961
struct X {
5062
x: u8,
5163
}

0 commit comments

Comments
 (0)