Detect implicitly defined late bound lifetime parameters as well
This commit is contained in:
parent
7ca378b251
commit
e40cedb393
5 changed files with 171 additions and 34 deletions
|
@ -772,6 +772,92 @@ fn trait_def<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
tcx.alloc_trait_def(def)
|
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(<.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>,
|
fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
def_id: DefId)
|
def_id: DefId)
|
||||||
-> &'tcx ty::Generics {
|
-> &'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 has_self = opt_self.is_some();
|
||||||
let mut parent_has_self = false;
|
let mut parent_has_self = false;
|
||||||
let mut parent_has_late_bound_regions = false;
|
|
||||||
let mut own_start = has_self as u32;
|
let mut own_start = has_self as u32;
|
||||||
let (parent_regions, parent_types) = parent_def_id.map_or((0, 0), |def_id| {
|
let (parent_regions, parent_types) = parent_def_id.map_or((0, 0), |def_id| {
|
||||||
let generics = tcx.generics_of(def_id);
|
let generics = tcx.generics_of(def_id);
|
||||||
assert_eq!(has_self, false);
|
assert_eq!(has_self, false);
|
||||||
parent_has_self = generics.has_self;
|
parent_has_self = generics.has_self;
|
||||||
parent_has_late_bound_regions = generics.has_late_bound_regions;
|
|
||||||
own_start = generics.count() as u32;
|
own_start = generics.count() as u32;
|
||||||
(generics.parent_regions + generics.regions.len() as u32,
|
(generics.parent_regions + generics.regions.len() as u32,
|
||||||
generics.parent_types + generics.types.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<_>>();
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
let has_late_bound_regions = regions.len() != ast_generics.lifetimes.len();
|
|
||||||
let object_lifetime_defaults =
|
let object_lifetime_defaults =
|
||||||
tcx.named_region_map.object_lifetime_defaults.get(&node_id);
|
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,
|
types: types,
|
||||||
type_param_to_index: type_param_to_index,
|
type_param_to_index: type_param_to_index,
|
||||||
has_self: has_self || parent_has_self,
|
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),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
36
src/test/compile-fail/constructor-lifetime-args.rs
Normal file
36
src/test/compile-fail/constructor-lifetime-args.rs
Normal 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
|
||||||
|
}
|
|
@ -42,25 +42,25 @@ fn method_call() {
|
||||||
//~| WARN this was previously accepted
|
//~| WARN this was previously accepted
|
||||||
|
|
||||||
S.late_implicit(&0, &0); // OK
|
S.late_implicit(&0, &0); // OK
|
||||||
// S.late_implicit::<'static>(&0, &0);
|
S.late_implicit::<'static>(&0, &0);
|
||||||
//FIXME ERROR cannot specify lifetime arguments explicitly
|
//~^ ERROR cannot specify lifetime arguments explicitly
|
||||||
//FIXME WARN this was previously accepted
|
//~| WARN this was previously accepted
|
||||||
// S.late_implicit::<'static, 'static>(&0, &0);
|
S.late_implicit::<'static, 'static>(&0, &0);
|
||||||
//FIXME ERROR cannot specify lifetime arguments explicitly
|
//~^ ERROR cannot specify lifetime arguments explicitly
|
||||||
//FIXME WARN this was previously accepted
|
//~| WARN this was previously accepted
|
||||||
// S.late_implicit::<'static, 'static, 'static>(&0, &0);
|
S.late_implicit::<'static, 'static, 'static>(&0, &0);
|
||||||
//FIXME ERROR cannot specify lifetime arguments explicitly
|
//~^ ERROR cannot specify lifetime arguments explicitly
|
||||||
//FIXME WARN this was previously accepted
|
//~| WARN this was previously accepted
|
||||||
S.late_implicit_early(&0); // OK
|
S.late_implicit_early(&0); // OK
|
||||||
S.late_implicit_early::<'static>(&0);
|
S.late_implicit_early::<'static>(&0);
|
||||||
//FIXME ERROR cannot specify lifetime arguments explicitly
|
//~^ ERROR cannot specify lifetime arguments explicitly
|
||||||
//FIXME WARN this was previously accepted
|
//~| WARN this was previously accepted
|
||||||
// S.late_implicit_early::<'static, 'static>(&0);
|
S.late_implicit_early::<'static, 'static>(&0);
|
||||||
//FIXME ERROR cannot specify lifetime arguments explicitly
|
//~^ ERROR cannot specify lifetime arguments explicitly
|
||||||
//FIXME WARN this was previously accepted
|
//~| WARN this was previously accepted
|
||||||
// S.late_implicit_early::<'static, 'static, 'static>(&0);
|
S.late_implicit_early::<'static, 'static, 'static>(&0);
|
||||||
//FIXME ERROR cannot specify lifetime arguments explicitly
|
//~^ ERROR cannot specify lifetime arguments explicitly
|
||||||
//FIXME WARN this was previously accepted
|
//~| WARN this was previously accepted
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ufcs() {
|
fn ufcs() {
|
||||||
|
@ -69,8 +69,8 @@ fn ufcs() {
|
||||||
//~| WARN this was previously accepted
|
//~| WARN this was previously accepted
|
||||||
|
|
||||||
S::late_implicit_early::<'static>(S, &0);
|
S::late_implicit_early::<'static>(S, &0);
|
||||||
//FIXME ERROR cannot specify lifetime arguments explicitly
|
//~^ ERROR cannot specify lifetime arguments explicitly
|
||||||
//FIXME WARN this was previously accepted
|
//~| WARN this was previously accepted
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -16,7 +16,17 @@ impl S {
|
||||||
fn early<'a, 'b>(self) -> (&'a u8, &'b u8) { loop {} }
|
fn early<'a, 'b>(self) -> (&'a u8, &'b u8) { loop {} }
|
||||||
fn late_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_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 {} }
|
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() {
|
fn method_call() {
|
||||||
|
@ -46,21 +56,26 @@ fn ufcs() {
|
||||||
|
|
||||||
S::late_implicit(S, &0, &0); // OK
|
S::late_implicit(S, &0, &0); // OK
|
||||||
S::late_implicit::<'static>(S, &0, &0);
|
S::late_implicit::<'static>(S, &0, &0);
|
||||||
//~^ ERROR expected at most 0 lifetime parameters, found 1 lifetime parameter
|
//~^ ERROR cannot specify lifetime arguments explicitly
|
||||||
//FIXME ERROR cannot specify lifetime arguments explicitly
|
|
||||||
S::late_implicit::<'static, 'static>(S, &0, &0);
|
S::late_implicit::<'static, 'static>(S, &0, &0);
|
||||||
//~^ ERROR expected at most 0 lifetime parameters, found 2 lifetime parameters
|
//~^ ERROR cannot specify lifetime arguments explicitly
|
||||||
//FIXME ERROR cannot specify lifetime arguments explicitly
|
|
||||||
S::late_implicit::<'static, 'static, 'static>(S, &0, &0);
|
S::late_implicit::<'static, 'static, 'static>(S, &0, &0);
|
||||||
//~^ ERROR expected at most 0 lifetime parameters, found 3 lifetime parameters
|
//~^ ERROR cannot specify lifetime arguments explicitly
|
||||||
//FIXME ERROR cannot specify lifetime arguments explicitly
|
|
||||||
S::late_implicit_early(S, &0); // OK
|
S::late_implicit_early(S, &0); // OK
|
||||||
S::late_implicit_early::<'static, 'static>(S, &0);
|
S::late_implicit_early::<'static, 'static>(S, &0);
|
||||||
//~^ ERROR expected at most 1 lifetime parameter, found 2 lifetime parameters
|
//~^ ERROR cannot specify lifetime arguments explicitly
|
||||||
//FIXME ERROR cannot specify lifetime arguments explicitly
|
|
||||||
S::late_implicit_early::<'static, 'static, 'static>(S, &0);
|
S::late_implicit_early::<'static, 'static, 'static>(S, &0);
|
||||||
//~^ ERROR expected at most 1 lifetime parameter, found 3 lifetime parameters
|
//~^ ERROR cannot specify lifetime arguments explicitly
|
||||||
//FIXME 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(S); // OK
|
||||||
S::early::<'static>(S);
|
S::early::<'static>(S);
|
||||||
|
@ -70,6 +85,9 @@ fn ufcs() {
|
||||||
let _: &u8 = S::life_and_type::<'static>(S);
|
let _: &u8 = S::life_and_type::<'static>(S);
|
||||||
S::life_and_type::<u8>(S);
|
S::life_and_type::<u8>(S);
|
||||||
S::life_and_type::<'static, 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() {}
|
fn main() {}
|
||||||
|
|
|
@ -369,7 +369,7 @@ impl Foo {
|
||||||
impl Foo {
|
impl Foo {
|
||||||
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
#[rustc_dirty(label="Hir", cfg="cfail2")]
|
||||||
#[rustc_clean(label="Hir", cfg="cfail3")]
|
#[rustc_clean(label="Hir", cfg="cfail3")]
|
||||||
#[rustc_metadata_dirty(cfg="cfail2")]
|
#[rustc_metadata_clean(cfg="cfail2")]
|
||||||
#[rustc_metadata_clean(cfg="cfail3")]
|
#[rustc_metadata_clean(cfg="cfail3")]
|
||||||
pub fn add_lifetime_parameter_to_method<'a>(&self) { }
|
pub fn add_lifetime_parameter_to_method<'a>(&self) { }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue