From e33bef685ef43cdefad624e73536927e766dec1e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 31 Aug 2015 11:11:51 +0200 Subject: [PATCH] lifetimes lint: walk type bounds as well as types (fixes #253, again) --- src/lifetimes.rs | 48 ++++++++++++++++++++++++--------- tests/compile-fail/lifetimes.rs | 12 +++++++++ 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/lifetimes.rs b/src/lifetimes.rs index bff0db14f7b..de7a39fdb3b 100644 --- a/src/lifetimes.rs +++ b/src/lifetimes.rs @@ -1,7 +1,7 @@ use syntax::ast::*; use rustc::lint::*; use syntax::codemap::Span; -use syntax::visit::{Visitor, walk_ty}; +use syntax::visit::{Visitor, walk_ty, walk_ty_param_bound}; use std::collections::HashSet; use utils::{in_external_macro, span_lint}; @@ -68,14 +68,7 @@ fn could_use_elision(func: &FnDecl, slf: Option<&ExplicitSelf>, // level of the current item. // check named LTs - let mut allowed_lts = HashSet::new(); - for lt in named_lts { - if lt.bounds.is_empty() { - allowed_lts.insert(Named(lt.lifetime.name)); - } - } - allowed_lts.insert(Unnamed); - allowed_lts.insert(Static); + let allowed_lts = allowed_lts_from(named_lts); // these will collect all the lifetimes for references in arg/return types let mut input_visitor = RefVisitor(Vec::new()); @@ -142,6 +135,18 @@ fn could_use_elision(func: &FnDecl, slf: Option<&ExplicitSelf>, false } +fn allowed_lts_from(named_lts: &[LifetimeDef]) -> HashSet { + let mut allowed_lts = HashSet::new(); + for lt in named_lts { + if lt.bounds.is_empty() { + allowed_lts.insert(Named(lt.lifetime.name)); + } + } + allowed_lts.insert(Unnamed); + allowed_lts.insert(Static); + allowed_lts +} + /// Number of unique lifetimes in the given vector. fn unique_lifetimes(lts: &[RefLt]) -> usize { lts.iter().collect::>().len() @@ -186,17 +191,34 @@ impl<'v> Visitor<'v> for RefVisitor { /// Are any lifetimes mentioned in the `where` clause? If yes, we don't try to /// reason about elision. fn has_where_lifetimes(where_clause: &WhereClause) -> bool { - let mut where_visitor = RefVisitor(Vec::new()); for predicate in &where_clause.predicates { match *predicate { WherePredicate::RegionPredicate(..) => return true, WherePredicate::BoundPredicate(ref pred) => { - walk_ty(&mut where_visitor, &pred.bounded_ty); + // a predicate like F: Trait or F: for<'a> Trait<'a> + let mut visitor = RefVisitor(Vec::new()); + // walk the type F, it may not contain LT refs + walk_ty(&mut visitor, &pred.bounded_ty); + if !visitor.0.is_empty() { return true; } + // if the bounds define new lifetimes, they are fine to occur + let allowed_lts = allowed_lts_from(&pred.bound_lifetimes); + // now walk the bounds + for bound in pred.bounds.iter() { + walk_ty_param_bound(&mut visitor, bound); + } + // and check that all lifetimes are allowed + for lt in visitor.into_vec() { + if !allowed_lts.contains(<) { + return true; + } + } } WherePredicate::EqPredicate(ref pred) => { - walk_ty(&mut where_visitor, &pred.ty); + let mut visitor = RefVisitor(Vec::new()); + walk_ty(&mut visitor, &pred.ty); + if !visitor.0.is_empty() { return true; } } } } - !where_visitor.into_vec().is_empty() + false } diff --git a/tests/compile-fail/lifetimes.rs b/tests/compile-fail/lifetimes.rs index ae115efec04..0b24ca65241 100755 --- a/tests/compile-fail/lifetimes.rs +++ b/tests/compile-fail/lifetimes.rs @@ -46,6 +46,18 @@ fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) { } // no error, bounde fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8) where 'b: 'a { } // no error, bounded lifetime +struct Lt<'a, I: 'static> { + x: &'a I +} + +fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> + where F: Fn(Lt<'a, I>) -> Lt<'a, I> // no error, fn bound references 'a +{ unreachable!() } + +fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> //~ERROR explicit lifetimes given + where for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I> +{ unreachable!() } + struct X { x: u8, }