From e40cedb3932060abf4496254959e9dd307eb6a2f Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 24 Jun 2017 23:30:05 +0300 Subject: [PATCH] Detect implicitly defined late bound lifetime parameters as well --- src/librustc_typeck/collect.rs | 91 ++++++++++++++++++- .../compile-fail/constructor-lifetime-args.rs | 36 ++++++++ .../method-call-lifetime-args-lint.rs | 38 ++++---- .../compile-fail/method-call-lifetime-args.rs | 38 ++++++-- src/test/incremental/hashes/inherent_impls.rs | 2 +- 5 files changed, 171 insertions(+), 34 deletions(-) create mode 100644 src/test/compile-fail/constructor-lifetime-args.rs diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 25707229dbc..32ccfc511fc 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -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(<.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::>(); - 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), }) } diff --git a/src/test/compile-fail/constructor-lifetime-args.rs b/src/test/compile-fail/constructor-lifetime-args.rs new file mode 100644 index 00000000000..50db9707355 --- /dev/null +++ b/src/test/compile-fail/constructor-lifetime-args.rs @@ -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 or the MIT license +// , 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::` is interpreted kinda like an associated item `S::::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 +} diff --git a/src/test/compile-fail/method-call-lifetime-args-lint.rs b/src/test/compile-fail/method-call-lifetime-args-lint.rs index 28f1035cad7..9bf34de92fe 100644 --- a/src/test/compile-fail/method-call-lifetime-args-lint.rs +++ b/src/test/compile-fail/method-call-lifetime-args-lint.rs @@ -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() {} diff --git a/src/test/compile-fail/method-call-lifetime-args.rs b/src/test/compile-fail/method-call-lifetime-args.rs index 00c03c3c859..c29701804a7 100644 --- a/src/test/compile-fail/method-call-lifetime-args.rs +++ b/src/test/compile-fail/method-call-lifetime-args.rs @@ -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 Fn(&'late u8)>) + -> &'a u8 { loop {} } + fn early_tricky_implicit<'a>(_: fn(&u8), + _: Box) + -> &'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::(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() {} diff --git a/src/test/incremental/hashes/inherent_impls.rs b/src/test/incremental/hashes/inherent_impls.rs index 370e07ba6c9..daddc0c9f54 100644 --- a/src/test/incremental/hashes/inherent_impls.rs +++ b/src/test/incremental/hashes/inherent_impls.rs @@ -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) { } } -- 2.44.0