Detect implicitly defined late bound lifetime parameters as well

This commit is contained in:
Vadim Petrochenkov 2017-06-24 23:30:05 +03:00
parent 7ca378b251
commit e40cedb393
5 changed files with 171 additions and 34 deletions

View file

@ -772,6 +772,92 @@ fn trait_def<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
tcx.alloc_trait_def(def)
}
fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
node: hir_map::Node<'tcx>)
-> bool {
struct LateBoundRegionsDetector<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
binder_depth: usize,
has_late_bound_regions: bool,
}
impl<'a, 'tcx> Visitor<'tcx> for LateBoundRegionsDetector<'a, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None
}
fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
if self.has_late_bound_regions { return }
match ty.node {
hir::TyBareFn(..) => {
self.binder_depth += 1;
intravisit::walk_ty(self, ty);
self.binder_depth -= 1;
}
_ => intravisit::walk_ty(self, ty)
}
}
fn visit_poly_trait_ref(&mut self,
tr: &'tcx hir::PolyTraitRef,
m: hir::TraitBoundModifier) {
if self.has_late_bound_regions { return }
self.binder_depth += 1;
intravisit::walk_poly_trait_ref(self, tr, m);
self.binder_depth -= 1;
}
fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) {
if self.has_late_bound_regions { return }
match self.tcx.named_region_map.defs.get(&lt.id).cloned() {
Some(rl::Region::Static) | Some(rl::Region::EarlyBound(..)) => {}
_ => self.has_late_bound_regions = true
}
}
}
fn has_late_bound_regions<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
generics: &'tcx hir::Generics,
decl: &'tcx hir::FnDecl)
-> bool {
let mut visitor = LateBoundRegionsDetector {
tcx, binder_depth: 0, has_late_bound_regions: false
};
for lifetime in &generics.lifetimes {
if tcx.named_region_map.late_bound.contains(&lifetime.lifetime.id) {
return true;
}
}
visitor.visit_fn_decl(decl);
visitor.has_late_bound_regions
}
match node {
hir_map::NodeTraitItem(item) => match item.node {
hir::TraitItemKind::Method(ref sig, _) =>
has_late_bound_regions(tcx, &sig.generics, &sig.decl),
_ => false,
},
hir_map::NodeImplItem(item) => match item.node {
hir::ImplItemKind::Method(ref sig, _) =>
has_late_bound_regions(tcx, &sig.generics, &sig.decl),
_ => false,
},
hir_map::NodeForeignItem(item) => match item.node {
hir::ForeignItemFn(ref fn_decl, _, ref generics) =>
has_late_bound_regions(tcx, generics, fn_decl),
_ => false,
},
hir_map::NodeItem(item) => match item.node {
hir::ItemFn(ref fn_decl, .., ref generics, _) =>
has_late_bound_regions(tcx, generics, fn_decl),
_ => false,
},
_ => false
}
}
fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId)
-> &'tcx ty::Generics {
@ -876,13 +962,11 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let has_self = opt_self.is_some();
let mut parent_has_self = false;
let mut parent_has_late_bound_regions = false;
let mut own_start = has_self as u32;
let (parent_regions, parent_types) = parent_def_id.map_or((0, 0), |def_id| {
let generics = tcx.generics_of(def_id);
assert_eq!(has_self, false);
parent_has_self = generics.has_self;
parent_has_late_bound_regions = generics.has_late_bound_regions;
own_start = generics.count() as u32;
(generics.parent_regions + generics.regions.len() as u32,
generics.parent_types + generics.types.len() as u32)
@ -900,7 +984,6 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
}).collect::<Vec<_>>();
let has_late_bound_regions = regions.len() != ast_generics.lifetimes.len();
let object_lifetime_defaults =
tcx.named_region_map.object_lifetime_defaults.get(&node_id);
@ -963,7 +1046,7 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
types: types,
type_param_to_index: type_param_to_index,
has_self: has_self || parent_has_self,
has_late_bound_regions: has_late_bound_regions || parent_has_late_bound_regions,
has_late_bound_regions: has_late_bound_regions(tcx, node),
})
}

View file

@ -0,0 +1,36 @@
// Copyright 2017 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.
// All lifetime parameters in struct constructors are currently considered early bound,
// i.e. `S::<ARGS>` is interpreted kinda like an associated item `S::<ARGS>::ctor`.
// This behavior is a bit weird, because if equivalent constructor were written manually
// it would get late bound lifetime parameters.
// Variant constructors behave in the same way, lifetime parameters are considered
// belonging to the enum and being early bound.
// https://github.com/rust-lang/rust/issues/30904
struct S<'a, 'b>(&'a u8, &'b u8);
enum E<'a, 'b> {
V(&'a u8),
U(&'b u8),
}
fn main() {
S(&0, &0); // OK
S::<'static>(&0, &0);
//~^ ERROR expected 2 lifetime parameters, found 1 lifetime parameter
S::<'static, 'static, 'static>(&0, &0);
//~^ ERROR expected at most 2 lifetime parameters, found 3 lifetime parameters
E::V(&0); // OK
E::V::<'static>(&0);
//~^ ERROR expected 2 lifetime parameters, found 1 lifetime parameter
E::V::<'static, 'static, 'static>(&0);
//~^ ERROR expected at most 2 lifetime parameters, found 3 lifetime parameters
}

View file

@ -42,25 +42,25 @@ fn method_call() {
//~| WARN this was previously accepted
S.late_implicit(&0, &0); // OK
// S.late_implicit::<'static>(&0, &0);
//FIXME ERROR cannot specify lifetime arguments explicitly
//FIXME WARN this was previously accepted
// S.late_implicit::<'static, 'static>(&0, &0);
//FIXME ERROR cannot specify lifetime arguments explicitly
//FIXME WARN this was previously accepted
// S.late_implicit::<'static, 'static, 'static>(&0, &0);
//FIXME ERROR cannot specify lifetime arguments explicitly
//FIXME WARN this was previously accepted
S.late_implicit::<'static>(&0, &0);
//~^ ERROR cannot specify lifetime arguments explicitly
//~| WARN this was previously accepted
S.late_implicit::<'static, 'static>(&0, &0);
//~^ ERROR cannot specify lifetime arguments explicitly
//~| WARN this was previously accepted
S.late_implicit::<'static, 'static, 'static>(&0, &0);
//~^ ERROR cannot specify lifetime arguments explicitly
//~| WARN this was previously accepted
S.late_implicit_early(&0); // OK
S.late_implicit_early::<'static>(&0);
//FIXME ERROR cannot specify lifetime arguments explicitly
//FIXME WARN this was previously accepted
// S.late_implicit_early::<'static, 'static>(&0);
//FIXME ERROR cannot specify lifetime arguments explicitly
//FIXME WARN this was previously accepted
// S.late_implicit_early::<'static, 'static, 'static>(&0);
//FIXME ERROR cannot specify lifetime arguments explicitly
//FIXME WARN this was previously accepted
//~^ ERROR cannot specify lifetime arguments explicitly
//~| WARN this was previously accepted
S.late_implicit_early::<'static, 'static>(&0);
//~^ ERROR cannot specify lifetime arguments explicitly
//~| WARN this was previously accepted
S.late_implicit_early::<'static, 'static, 'static>(&0);
//~^ ERROR cannot specify lifetime arguments explicitly
//~| WARN this was previously accepted
}
fn ufcs() {
@ -69,8 +69,8 @@ fn ufcs() {
//~| WARN this was previously accepted
S::late_implicit_early::<'static>(S, &0);
//FIXME ERROR cannot specify lifetime arguments explicitly
//FIXME WARN this was previously accepted
//~^ ERROR cannot specify lifetime arguments explicitly
//~| WARN this was previously accepted
}
fn main() {}

View file

@ -16,7 +16,17 @@ fn late_implicit(self, _: &u8, _: &u8) {}
fn early<'a, 'b>(self) -> (&'a u8, &'b u8) { loop {} }
fn late_early<'a, 'b>(self, _: &'a u8) -> &'b u8 { loop {} }
fn late_implicit_early<'b>(self, _: &u8) -> &'b u8 { loop {} }
fn late_implicit_self_early<'b>(&self) -> &'b u8 { loop {} }
fn late_unused_early<'a, 'b>(self) -> &'b u8 { loop {} }
fn life_and_type<'a, T>(self) -> &'a T { loop {} }
// 'late lifetimes here belong to nested types not to the tested functions.
fn early_tricky_explicit<'a>(_: for<'late> fn(&'late u8),
_: Box<for<'late> Fn(&'late u8)>)
-> &'a u8 { loop {} }
fn early_tricky_implicit<'a>(_: fn(&u8),
_: Box<Fn(&u8)>)
-> &'a u8 { loop {} }
}
fn method_call() {
@ -46,21 +56,26 @@ fn ufcs() {
S::late_implicit(S, &0, &0); // OK
S::late_implicit::<'static>(S, &0, &0);
//~^ ERROR expected at most 0 lifetime parameters, found 1 lifetime parameter
//FIXME ERROR cannot specify lifetime arguments explicitly
//~^ ERROR cannot specify lifetime arguments explicitly
S::late_implicit::<'static, 'static>(S, &0, &0);
//~^ ERROR expected at most 0 lifetime parameters, found 2 lifetime parameters
//FIXME ERROR cannot specify lifetime arguments explicitly
//~^ ERROR cannot specify lifetime arguments explicitly
S::late_implicit::<'static, 'static, 'static>(S, &0, &0);
//~^ ERROR expected at most 0 lifetime parameters, found 3 lifetime parameters
//FIXME ERROR cannot specify lifetime arguments explicitly
//~^ ERROR cannot specify lifetime arguments explicitly
S::late_implicit_early(S, &0); // OK
S::late_implicit_early::<'static, 'static>(S, &0);
//~^ ERROR expected at most 1 lifetime parameter, found 2 lifetime parameters
//FIXME ERROR cannot specify lifetime arguments explicitly
//~^ ERROR cannot specify lifetime arguments explicitly
S::late_implicit_early::<'static, 'static, 'static>(S, &0);
//~^ ERROR expected at most 1 lifetime parameter, found 3 lifetime parameters
//FIXME ERROR cannot specify lifetime arguments explicitly
//~^ ERROR cannot specify lifetime arguments explicitly
S::late_implicit_self_early(&S); // OK
S::late_implicit_self_early::<'static, 'static>(&S);
//~^ ERROR cannot specify lifetime arguments explicitly
S::late_implicit_self_early::<'static, 'static, 'static>(&S);
//~^ ERROR cannot specify lifetime arguments explicitly
S::late_unused_early(S); // OK
S::late_unused_early::<'static, 'static>(S);
//~^ ERROR cannot specify lifetime arguments explicitly
S::late_unused_early::<'static, 'static, 'static>(S);
//~^ ERROR cannot specify lifetime arguments explicitly
S::early(S); // OK
S::early::<'static>(S);
@ -70,6 +85,9 @@ fn ufcs() {
let _: &u8 = S::life_and_type::<'static>(S);
S::life_and_type::<u8>(S);
S::life_and_type::<'static, u8>(S);
S::early_tricky_explicit::<'static>(loop {}, loop {}); // OK
S::early_tricky_implicit::<'static>(loop {}, loop {}); // OK
}
fn main() {}

View file

@ -369,7 +369,7 @@ pub fn add_lifetime_parameter_to_method(&self) { }
impl Foo {
#[rustc_dirty(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_metadata_dirty(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail2")]
#[rustc_metadata_clean(cfg="cfail3")]
pub fn add_lifetime_parameter_to_method<'a>(&self) { }
}